前言
最近得空一直在搗鼓Vue3開發,想要快速上手,還是得寫一些自定義元件。之前就有基於vue2.x寫過一些自定義Navbar+Tabbar及彈窗元件。於是就擼起袖子開整。
今天主要是給大家分享一些如何快速上手Vue3開發。
目前Vue3的最新版本是V3.0.4,star高達19.7K+。而且更新還很頻繁。
# vue3官網https://v3.vuejs.org/# vue3中文網https://v3.cn.vuejs.org/# vue3倉庫地址https://github.com/vuejs/vue-next# Vite構建工具https://vite-design.surge.sh/
透過如下2種方法快速建立Vue3專案。
1、Vite構建工具
$ npm init vite-app <project-name>$ cd <project-name>$ npm install$ npm run dev
如果安裝了Vite腳手架 create-vite-app,也可以透過下面方法快速建立。
$ create-vite-app <project-name>
2、@vue/cli腳手架
透過CLI建立需要版本是V4.5以上。升級CLI,npm i @vue/cli -g
$ vue create <project-name># 選擇 Vue3選項 即可$ npm run serve
下面進入今天的主題,Vue3實現自定義導航欄+底部Tab+彈窗元件。
在components目錄下新建headerBar.vue和tabBar.vue兩個頁面。
並新建一個components.js頁面用於引入公共元件。
然後在main.js中註冊即可。
1、自定義headerBar.vue其實這個元件和vue2.x建立沒啥區別。
<template> <div class="tab-bar" :class="{'fixed': fixed}"> <div class="tab-bar__wrap flexbox flex-alignc" :style="{'background': bgcolor}"> <div v-for="(item, index) in tabs" :key="index" class="navigator" :class="currentTabIndex == index ? 'on' : ''" @click="switchTabs(index, item)"> <div class="ico" :class="{'dock': item.dock}"> <i v-if="item.dock" class="dock-bg" :style="{'background': item.dockBg ? item.dockBg : activeColor}"></i> <i v-if="item.icon" class="iconfont" :class="item.icon" :style="{'color': (currentTabIndex == index && !item.dock ? activeColor : color), 'font-size': item.iconSize}"></i> <img v-if="item.img" class="iconimg" :src="currentTabIndex == index && !item.dock ? item.activeImg : item.img" :style="{'font-size': item.iconSize}" /> <em v-if="item.badge" class="nuxt__badge">{{item.badge}}</em> <em v-if="item.dot" class="nuxt__badge-dot"></em> </div> <div class="txt" :style="{'color': (currentTabIndex == index ? activeColor: color)}">{{item.title}}</div> </div> </div> </div></template><script>import { ref } from 'vue'import { useRouter, useRoute } from 'vue-router' export default { props: { current: { type: [Number, String], default: 0 }, // 背景色 bgcolor: { type: String, default: '#fff' }, // 顏色 color: { type: String, default: '#999' }, // 啟用顏色 activeColor: { type: String, default: '#22d59c' }, // 是否固定 fixed: { type: [Boolean, String], default: false }, // tab選項 tabs: { type: Array, default: () => null }, }, emits: { click: null }, setup(props, context) { const currentTabIndex = ref(props.current) const router = useRouter() const route = useRoute() // 匹配當前路由頁面 const _curPath = route.path props.tabs.map((item, index) => { if(item.path == _curPath) { currentTabIndex.value = index } }) const switchTabs = (index, item) => { currentTabIndex.value = index context.emit('click', index) if(item.path) { router.push(item.path) } } return { currentTabIndex, switchTabs, } } }</script>
支援自定義背景色、文字顏色|選中顏色、是否固定、點選選項(返回索引值)。
另外還支援dock效果,圖示支援iconfont及圖片。
<tab-bar bgcolor="#f9f9f9" color="#f00" @click="tabbarClicked" :tabs="[ { icon: 'icon-home', title: '首頁', path: '/index', }, { icon: 'icon-guanli', title: '管理', path: '/manage' badge: 1 }, { icon: 'icon-fabu', title: '釋出', dock: true, dockBg: '#f60', iconSize: '20px', }, { icon: 'icon-guanli', title: '聯絡人', img: 'http://www.xxx.com/contact.jpg', activeImg: 'http://www.xxx.com/contact_on.jpg', }, { icon: 'icon-me', title: '我的', dot: true }]"/>// 點選事件tabbarClicked(index) { console.log('tabbar索引值:' + index)}
3、自定義V3Popup元件V3Popup 基於Vue3開發的一款集合Alert、Dialog、ActionSheet、Toast等功能的移動端Vue3.0彈出框元件。
支援標籤式+函式式兩種呼叫方式。
// 標籤式呼叫<v3-popup v-model="showAlert" title="標題" content="彈窗內容資訊" type="ios" shadeClose="false" xclose z-index="2021" :btns="[ {...}, {...}, ]"/>
// 函式式呼叫let $el = this.$v3popup({ title: '標題', content: '彈窗內容資訊', type: 'ios', shadeClose: false, xclose: true, zIndex: 2021, btns: [ {text: '關閉'}, { text: '確定', style: 'color:#09f;', click: () => { $el.close() } }, ]});
template模板寫法和vue2.x沒什麼區別,主要是邏輯處理部分,既可以使用vue2.x的寫法,也可以使用vue3的Composition API寫法。
下面主要是使用Vue3寫法實現邏輯。
<script> import { ref, reactive, watch, toRefs, nextTick, onMounted } from 'vue' /** * Vue3.0寫法 */ let $index = 0, $locknum = 0, $timer = {} export default { props: { // 接收父元件v-model值,如果v-model:xxx,則這裡需寫xxx: {...} modelValue: { type: Boolean, default: false }, // 彈框識別符號,相同ID共享一個例項 id: { type: String, default: '' }, title: String, content: String, type: String, popupStyle: String, icon: String, shade: { type: [Boolean, String], default: true }, shadeClose: { type: [Boolean, String], default: true }, opacity: { type: [Number, String], default: '' }, round: Boolean, xclose: Boolean, xposition: { type: String, default: 'right' }, xcolor: { type: String, default: '#333' }, anim: { type: String, default: 'scaleIn' }, position: String, follow: { type: Array, default: null }, time: { type: [Number, String], default: 0 }, zIndex: { type: [Number, String], default: '8080' }, teleport: [String, Object], btns: { type: Array, default: null }, onSuccess: { type: Function, default: null }, onEnd: { type: Function, default: null }, // 接收函式式移除指令 remove: Function, }, emits: [ 'update:modelValue', ], setup(props, context) { const elRef = ref(null) const data = reactive({ opened: false, closeCls: '', toastIcon: { //... } }) watch(() => props.modelValue, (val) => { if(val) { open() } else { close() } }) onMounted(() => { if(props.modelValue) { open() } }) // 開啟彈框 const open = () => { if(data.opened) return data.opened = true typeof props.onSuccess === 'function' && props.onSuccess() const dom = elRef.value dom.style.zIndex = getZIndex() + 1 if(JSON.parse(props.shade)) { if(!document.body.classList.contains('vui__body-hidden')) { document.body.classList.add('vui__body-hidden') } $locknum++ } // 倒計時關閉 if(props.time) { $index++ // 避免重複點選 if($timer[$index] !== null) clearTimeout($timer[$index]) $timer[$index] = setTimeout(() => { close() }, parseInt(props.time) * 1000) } //... } // 關閉彈框 const close = () => { data.closeCls = true setTimeout(() => { data.opened = false data.closeCls = false if(JSON.parse(props.shade)) { $locknum-- if(!$locknum) { document.body.classList.remove('vui__body-hidden') } } if(props.time) { $index-- } context.emit('update:modelValue', false) typeof props.onEnd === 'function' && props.onEnd() }, 200) } // 點選遮罩層 const shadeClicked = () => { if(JSON.parse(props.shadeClose)) { close() } } // 按鈕事件 const btnClicked = (e, index) => { //... } // 獲取彈窗層級 const getZIndex = () => { //... } // 獲取彈窗座標點 const getPos = (x, y, ow, oh, winW, winH) => { let l = (x + ow) > winW ? x - ow : x; let t = (y + oh) > winH ? y - oh : y; return [l, t]; } return { ...toRefs(data), shadeClicked, btnClicked, close, elRef, } } }</script>
vue3中watch監聽事件,可以監聽一個、多個值(物件/陣列)。
<script> import { ref, computed, reactive, toRefs, watch, nextTick } from 'vue' export default { setup() { const count = ref(0) const data = reactive({ num: 0, total: computed(() => data.num * 2), handlePlus: () => { data.num++ } }) // 監聽器watch使用 // 1、監聽ref一個值count // watch(count, () => {}) watch(count, (newVal, oldVal) => { console.log('count updated:' + count.value) console.log(newVal, oldVal, "--新舊值"); }) // 監聽reactive中某一項 watch(() => data.num, (newVal, oldVal) => { console.log('+++count updated:' + data.num) console.log(newVal, oldVal, "==新舊值"); }) // 2、監聽整個reactive中data物件 watch(data, () => { console.log('==count updated:' + data.num) }) // 3、監聽多個值 // watch([count, data], () => {}) watch([count, () => data.num], () => { console.log('某一項updated:' + count.value + '---' + data.num) }) return { //... } } }</script>
注意:Vue3不支援div的slot插槽。
最新評論