基于Vue3.0開發(fā)輕量級手機端彈框組件V3Popup的場景分析
之前有分享一個vue2.x移動端彈框組件,今天給大家?guī)淼氖荲ue3實現(xiàn)自定義彈框組件。
V3Popup 基于vue3.x實現(xiàn)的移動端彈出框組件,集合msg、alert、dialog、modal、actionSheet、toast等多種效果。支持20+種自定義參數(shù)配置,旨在通過極簡的布局、精簡的調(diào)用方式解決多樣化的彈框場景。

v3popup 在開發(fā)之初參考借鑒了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同樣支持標簽式+函數(shù)式兩種調(diào)用方式。
標簽寫法
<v3-popup
v-model="showDialog"
title="標題"
content="<p style='color:#df6a16;padding:10px;'>這里是內(nèi)容信息!</p>"
type="android"
shadeClose="false"
xclose
:btns="[
{text: '取消', click: () => showDialog=false},
{text: '確認', style: 'color:#f90;', click: handleOK},
]"
@success="handleOpen"
@end="handleClose"
/>
<template #content>這里是自定義插槽內(nèi)容信息!</template>
</v3-popup>
函數(shù)寫法
let $el = this.$v3popup({
title: '標題',
content: '<p style='color:#df6a16;padding:10px;'>這里是內(nèi)容信息!</p>',
type: 'android',
shadeClose: false,
xclose: true,
btns: [
{text: '取消', click: () => { $el.close(); }},
{text: '確認', style: 'color:#f90;', click: () => handleOK},
],
onSuccess: () => {},
onEnd: () => {}
})
Vue3.0中掛載全局函數(shù)有2種方式app.config.globalPropertiesapp.provide
通過 app.config.globalProperties.$v3popup = V3Popup 方式掛載。
// vue2.x中調(diào)用
methods: {
showDialog() {
this.$v3popup({...})
}
}
// vue3.x中調(diào)用
setup() {
// 獲取上下文
const { ctx } = getCurrentInstance()
ctx.$v3popup({...})
}
通過 app.provide('v3popup', V3Popup) 方式掛載。
// vue2.x中調(diào)用
methods: {
showDialog() {
this.v3popup({...})
}
}
// vue3.x中調(diào)用
setup() {
const v3popup = inject('v3popup')
const showDialog = () => {
v3popup({...})
}
return {
v3popup,
showDialog
}
}
不過vue.js作者是推薦使用 provide inject 方式來掛載原型鏈函數(shù)。
◆ 效果預覽








◆ 參數(shù)配置
v3popup支持如下參數(shù)配置。
|props參數(shù)| v-model 是否顯示彈框 title 標題 content 內(nèi)容(支持String、帶標簽內(nèi)容、自定義插槽內(nèi)容)***如果content內(nèi)容比較復雜,推薦使用標簽式寫法 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 彈窗自動關閉秒數(shù)(1、2、3) zIndex 彈窗層疊(默認8080) teleport 指定掛載節(jié)點(默認是掛載組件標簽位置,可通過teleport自定義掛載位置) teleport="body | #xxx | .xxx" btns 彈窗按鈕(參數(shù):text|style|disabled|click) ++++++++++++++++++++++++++++++++++++++++++++++ |emit事件觸發(fā)| success 層彈出后回調(diào)(@success="xxx") end 層銷毀后回調(diào)(@end="xxx") ++++++++++++++++++++++++++++++++++++++++++++++ |event事件| onSuccess 層打開回調(diào)事件 onEnd 層關閉回調(diào)事件
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(() => {
...
})
// 監(jiān)聽彈層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來實現(xiàn)函數(shù)式調(diào)用。
import { createApp } from 'vue'
import PopupConstructor from './popup.vue'
let $inst
// 創(chuàng)建掛載實例
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)
}
這樣就實現(xiàn)了在vue3中注冊原型鏈函數(shù)和v3-popup組件,就可以使用函數(shù)式調(diào)用了。

到此這篇關于基于Vue3.0開發(fā)輕量級手機端彈框組件V3Popup的場景分析的文章就介紹到這了,更多相關Vue3自定義彈框組件內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
關于vue.extend和vue.component的區(qū)別淺析
最近工作中遇到了vue.extend,vue.component,但二者之間的區(qū)別與聯(lián)系是什么呢?下面這篇文章主要給大家介紹了關于vue.extend和vue.component區(qū)別的相關資料,需要的朋友可以參考借鑒,下面來一起看看吧。2017-08-08
Vue elementUI 自定義表單模板組件功能實現(xiàn)
在項目開發(fā)中,我們會遇到這種需求,在管理后臺添加自定義表單,在指定的頁面使用定義好的表單,這篇文章主要介紹了Vue elementUI 自定義表單模板組件,需要的朋友可以參考下2022-12-12
Vue環(huán)境搭建+VSCode+Win10的詳細教程
這篇文章主要介紹了Vue環(huán)境搭建+VSCode+Win10,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-08-08
Vue項目開發(fā)實現(xiàn)父組件與子組件數(shù)據(jù)間的雙向綁定原理及適用場景
在 Vue.js 中,實現(xiàn)父組件與子組件數(shù)據(jù)之間的雙向綁定,可以通過以下幾種方式,下面我將介紹幾種常見的方法,并解釋它們的實現(xiàn)原理和適用場景,感興趣的朋友跟隨小編一起看看吧2024-12-12

