Vue3 + vant 二次封装弹窗,实现函数式调用

1,355 阅读2分钟

需求背景

很多情况下因为业务的要求,我们可能需要在第三方组件库的基础上进行二次封装,使其功能或样式更加符合我们的业务场景

举一个简单的例子,假设我们需要在 vant-dialog 定制自己的内容,比如说在右上角加个关闭按钮,如果正常的引用组件方法,我们需要在每个使用的页面引用组件的结构,这样比较麻烦

// 引用组件的页面
<van-dialog v-model:show="show" title="标题" show-cancel-button>
  <img src="https://fastly.jsdelivr.net/npm/@vant/assets/apple-3.jpeg" />
</van-dialog>

import { ref } from 'vue';

export default {
  setup() {
    const show = ref(false);
    return { show };
  },
};

我们期望这样使用, 使用函数式直接调用那就方便不少

import { showMyDialog } from '@/components/showMyDialog'
export default {
  setup() {
    showMyDialog({ msg: '弹窗内容' })
  },
};

怎么实现呢? 主要分为两步:

  1. 封装 MyDialog 组件
  2. 实现 showMyDialog 函数

封装 vant-dialog 弹窗

二次封装 vant-dialog,思路基本就是根据自己的业务需求对样式或功能进行定制,我们的场景并不需要对所有属性进行透传,这里根据自己的实际情况进行改造就好

<template>
    <van-dialog
        class="my-dialog"
        v-model:show="localShow"
        :show-cancel-button="showCancelButton"
        @confirm="handleConfirm"
        @cancel="handleCancel">
        <div class="dialog-wrap">
            <div class="header">
                <van-icon @click="handleCancel" class="close-btn" name="close" />
            </div>
            <div class="content">
                <p>{{msg}}</p>
            </div>
        </div>
    </van-dialog>
</template>

<script setup lang="ts">
import { ref } from 'vue'
interface Props {
    visible: boolean
    _close?: () => void
    _confirm?: () => void
    msg: string
    showCancelButton?: boolean
}

const props = defineProps<Props>()

// 将外部传入的 visible 状态转换为本地响应式变量
const localShow = ref(props.visible)

const emit = defineEmits(['update:visible'])

const handleConfirm = () => {
    props._confirm?.()
    emit('update:visible', false)
}

const handleCancel = () => {
    emit('update:visible', false)
    props._close?.()
}
</script>

封装 showMyDialog 函数

  1. 实现 showMyDialog 函数:

    • 创建一个 DOM 节点(mountNode),用于挂载新的 Vue 应用实例。

    • 使用 createApp 函数创建一个新的 Vue 应用实例,并将 MyDialog.vue 作为根组件,传入的 props 包括:

      • 设置 visible 属性为 true 使对话框初始化时可见
      • 将外部传入的 msgtype 和 showCancelButton 分配给组件相应的 prop
      • _close 和 _confirm 回调函数分别处理点击关闭和确认操作,在内部进行卸载应用实例、移除 DOM 节点等清理工作,并触发外部提供的 close 或 confirm 回调函数(如果存在)
    • 将新创建的 mountNode 添加到文档的 body 元素中,确保对话框能正确显示在页面上。

    • 最后,调用 mount 方法将 Vue 应用实例挂载到 mountNode 上,完成弹窗组件的渲染。

import { type App, createApp } from 'vue'
import MyDialog from './MyDialog.vue'

interface IMyDialog {
    close?: () => void
    confirm?: () => void
    msg: string
    showCancelButton?: boolean
}

export function showMyDialog({ close, confirm, msg, type, showCancelButton }: IMyDialog) {
    let mountNode = document.createElement('div')
    let dialogApp: App<Element> | undefined = createApp(MyDialog, {
        visible: true,
        msg,
        type,
        showCancelButton,
        _close: () => {
            if (dialogApp) {
                dialogApp.unmount()
                document.body.removeChild(mountNode)
                dialogApp = undefined
                close?.()
            }
        },
        _confirm: () => {
            confirm?.()
            dialogApp?.unmount()
            document.body.removeChild(mountNode)
            dialogApp = undefined
        },
    })
    document.body.appendChild(mountNode)
    dialogApp.mount(mountNode)
}

使用

import { showMyDialog } from '@/components/showMyDialog'

showMyDialog({ msg: '测试弹窗', confirm: () => { console.log('点击了确认'); }})