介绍

V3Popup 一款基于vue3.0开发手机端弹出框组件。在开发设计之初参考借鉴了Vant3Antdv2.0中弹框组件化思维。反对超过20+种参数灵便配置。


引入组件

// 在main.js中全局引入import { createApp } from 'vue'import App from './App.vue'// 引入弹窗组件v3popupimport V3Popup from './components/v3popup' createApp(App).use(V3Popup).mount('#app')

遵循极简的调用形式,反对组件式+函数式两种调用。

  • 组件式
<!-- 提示框 --><v3-popup v-model="showMsg" anim="fadeIn" content="msg提示框测试(3s后窗口敞开)" shadeClose="false" time="3" /> <!-- 询问框 --><v3-popup v-model="showConfirm" shadeClose="false" title="题目" xclose z-index="2020"    content="<div style='color:#1be769;padding:20px;'>确认框(这里是确认框提示信息,这里确认框提示信息,这里是确认框提示信息)</div>"    :btns="[        {text: '勾销', click: () => showConfirm=false},        {text: '确定', style: 'color:#f90;', click: handleInfo},    ]"/>
  • 函数式
let $el = this.$v3popup({    title: '题目',    content: '<div style='color:#f90;padding:10px;'>这里是内容信息!</div>',    type: 'android',    shadeClose: false,    xclose: true,    btns: [        {text: '勾销', click: () => { $el.close(); }},        {text: '确认', style: 'color:#f90;', click: () => handleOK},    ],    onSuccess: () => {},    onEnd: () => {}})

vue2中能够通过prototype原型链挂载全局办法。vue3如果挂载全局办法呢?
其实vue3中提供了两种形式来挂载全局办法。
两种形式。
1、通过app.config.globalProperties.$v3popup = V3Popup

// vue2中调用methods: {    showDialog() {        this.$v3popup({...})    }} // vue3中调用setup() {    // 获取上下文    const { ctx } = getCurrentInstance()    ctx.$v3popup({...})}

2、通过app.provide('v3popup', V3Popup)

// vue2中调用methods: {    showDialog() {        this.v3popup({...})    }} // vue3中调用setup() {    const v3popup = inject('v3popup')        const showDialog = () => {        v3popup({...})    }     return {        v3popup,        showDialog    }}

不过vue作者更举荐第二种形式挂载办法。

预览图










参数配置

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           层敞开回调事件
<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中如何把弹框实例挂载到body上呢?其实可通过createAppcreateVNode实现。
createApp第一个参数传入组件模板,第二个参数传入自定义参数即可。

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)}

通过如上办法就能够实现函数式调用组件了。

okey,基于Vue3.x开发自定义弹出层组件就分享到这里。心愿能喜爱~✍