基於Vue3.0開發的輕量級手機端彈框元件V3Popup。
之前有分享一個vue2.x移動端彈框元件,今天給大家帶來的是Vue3實現自定義彈框元件。
V3Popup 基於vue3.x實現的移動端彈出框元件,集合msg、alert、dialog、modal、actionSheet、toast等多種效果。支援20+種自定義引數配置,旨在通過極簡的佈局、精簡的呼叫方式解決多樣化的彈框場景。
v3popup 在開發之初參考借鑑了Vant3、ElementPlus等元件化思想。並且功能效果和之前vue2.0保持一致。
◆ 快速引入
在main.js中全域性引入v3popup元件。
import { createApp } from 'vue' import App from './App.vue' const app = createApp(App) // 引入彈窗元件v3popup import V3Popup from './components/v3popup' app.use(V3Popup) app.mount('#app')
v3popup同樣支援標籤式+函式式兩種呼叫方式。
- 標籤寫法
<v3-popup v-model="showDialog" title="標題" content="<p style='color:#df6a16;padding:10px;'>這裡是內容資訊!</p>" type="android" shadeClose="false" xclose :btns="[ {text: '取消', click: () => showDialog=false}, {text: '確認', style: 'color:#f90;', click: handleOK}, ]" @success="handleOpen" @end="handleClose" /> <template #content>這裡是自定義插槽內容資訊!</template> </v3-popup>
- 函式寫法
let $el = this.$v3popup({ title: '標題', content: '<p style='color:#df6a16;padding:10px;'>這裡是內容資訊!</p>', type: 'android', shadeClose: false, xclose: true, btns: [ {text: '取消', click: () => { $el.close(); }}, {text: '確認', style: 'color:#f90;', click: () => handleOK}, ], onSuccess: () => {}, onEnd: () => {} })
Vue3.0中掛載全域性函式有2種方式 app.config.globalProperties app.provide
通過 app.config.globalProperties.$v3popup = V3Popup 方式掛載。
// vue2.x中呼叫 methods: { showDialog() { this.$v3popup({...}) } } // vue3.x中呼叫 setup() { // 獲取上下文 const { ctx } = getCurrentInstance() ctx.$v3popup({...}) }
通過 app.provide('v3popup', V3Popup) 方式掛載。
// vue2.x中呼叫 methods: { showDialog() { this.v3popup({...}) } } // vue3.x中呼叫 setup() { const v3popup = inject('v3popup') const showDialog = () => { v3popup({...}) } return { v3popup, showDialog } }
不過vue.js作者是推薦使用 provide inject 方式來掛載原型鏈函式。
◆ 效果預覽
◆ 引數配置
v3popup支援如下引數配置。
|props引數| v-model 是否顯示彈框 title 標題 content 內容(支援String、帶標籤內容、自定義插槽內容)***如果content內容比較複雜,推薦使用標籤式寫法 type 彈窗型別(toast | footer | actionsheet | actionsheetPicker | android | ios) popupStyle 自定義彈窗樣式 icon toast圖示(loading | success | fail) shade 是否顯示遮罩層 shadeClose 是否點選遮罩時關閉彈窗 opacity 遮罩層透明度 round 是否顯示圓角 xclose 是否顯示關閉圖示 xposition 關閉圖示位置(left | right | top | bottom) xcolor 關閉圖示顏色 anim 彈窗動畫(scaleIn | fadeIn | footer | fadeInUp | fadeInDown) position 彈出位置(top | right | bottom | left) follow 長按/右鍵彈窗(座標點) time 彈窗自動關閉秒數(1、2、3) zIndex 彈窗層疊(預設8080) teleport 指定掛載節點(預設是掛載元件標籤位置,可通過teleport自定義掛載位置) teleport="body | #xxx | .xxx" btns 彈窗按鈕(引數:text|style|disabled|click) ++++++++++++++++++++++++++++++++++++++++++++++ |emit事件觸發| success 層彈出後回撥(@success="xxx") end 層銷燬後回撥(@end="xxx") ++++++++++++++++++++++++++++++++++++++++++++++ |event事件| onSuccess 層開啟回撥事件 onEnd 層關閉回撥事件
v3popup.vue模板
<template> <div ref="elRef" v-show="opened" class="vui__popup" :class="{'vui__popup-closed': closeCls}" :id="id"> <!-- //蒙層 --> <div v-if="JSON.parse(shade)" class="vui__overlay" @click="shadeClicked" :style="{opacity}"></div> <div class="vui__wrap"> <div class="vui__wrap-section"> <div class="vui__wrap-child" :class="['anim-'+anim, type&&'popupui__'+type, round&&'round', position]" :style="[popupStyle]"> <div v-if="title" class="vui__wrap-tit" v-html="title"></div> <div v-if="type=='toast'&&icon" class="vui__toast-icon" :class="['vui__toast-'+icon]" v-html="toastIcon[icon]"></div> <!-- 判斷插槽是否存在 --> <template v-if="$slots.content"> <div class="vui__wrap-cnt"><slot name="content" /></div> </template> <template v-else> <div v-if="content" class="vui__wrap-cnt" v-html="content"></div> </template> <slot /> <div v-if="btns" class="vui__wrap-btns"> <span v-for="(btn, index) in btns" :key="index" class="btn" :style="btn.style" @click="btnClicked($event, index)" v-html="btn.text"></span> </div> <span v-if="xclose" class="vui__xclose" :class="xposition" :style="{'color': xcolor}" @click="close"></span> </div> </div> </div> </div> </template>
/** * @Desc Vue3.0自定義彈框元件V3Popup * @Time andy by 2020-12 * @About Q:282310962 wx:xy190310 */ <script> import { onMounted, ref, reactive, watch, toRefs, nextTick } from 'vue' let $index = 0, $locknum = 0, $timer = {} export default { props: { // 接收父元件v-model值,如果v-model:open,則這裡需寫open: {...} 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 }, }, emits: [ 'update:modelValue' ], setup(props, context) { const elRef = ref(null) const data = reactive({ opened: false, closeCls: '', toastIcon: { ... } }) onMounted(() => { ... }) // 監聽彈層v-model watch(() => props.modelValue, (val) => { if(val) { open() }else { close() } }) // 開啟彈層 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(props.time) { $index++ // 避免重複操作 if($timer[$index] !== null) clearTimeout($timer[$index]) $timer[$index] = setTimeout(() => { close() }, parseInt(props.time) * 1000) } // 長按|右鍵選單 if(props.follow) { ... } } // 關閉彈層 const close = () => { if(!data.opened) return data.closeCls = true setTimeout(() => { ... context.emit('update:modelValue', false) typeof props.onEnd === 'function' && props.onEnd() }, 200) } // 點選遮罩層 const shadeClicked = () => { if(JSON.parse(props.shadeClose)) { close() } } // 按鈕事件 const btnClicked = (e, index) => { let btn = props.btns[index]; if(!btn.disabled) { typeof btn.click === 'function' && btn.click(e) } } ... return { ...toRefs(data), elRef, close, shadeClicked, btnClicked, } } } </script>
Vue3中可通過 createApp 或 createVNode | render 來掛載例項到body來實現函式式呼叫。
import { createApp } from 'vue' import PopupConstructor from './popup.vue' let $inst // 建立掛載例項 let createMount = (opts) => { const mountNode = document.createElement('div') document.body.appendChild(mountNode) const app = createApp(PopupConstructor, { ...opts, modelValue: true, remove() { app.unmount(mountNode) document.body.removeChild(mountNode) } }) return app.mount(mountNode) } function V3Popup(options = {}) { options.id = options.id || 'v3popup_' + generateId() $inst = createMount(options) return $inst } V3Popup.install = app => { app.component('v3-popup', PopupConstructor) // app.config.globalProperties.$v3popup = V3Popup app.provide('v3popup', V3Popup) }
這樣就實現了在vue3中註冊原型鏈函式和v3-popup元件,就可以使用函式式呼叫了。
好了,基於Vue3開發自定義彈框元件就分享到這裡。希望對大家有些幫助哈!?
最後附上一個Next.js例項專案
Next+React.js桌面端聊天:https://www.cnblogs.com/xiaoyan2017/p/14195483.html