Vue 3 适配改造Vue 2编程式创建组件实例的showPrompt函数方案咨询
我太懂你现在的困扰了——Vue3砍掉了$on、$destroy这些实例方法,而且app.component也不再返回组件构造器,原来那套编程式创建Modal的逻辑直接就失效了对吧?我之前刚帮团队改造过类似的弹窗函数,给你一套完整的适配方案:
核心思路转变
Vue3推荐用渲染函数API(createVNode+render)来动态创建组件,替代Vue2里直接实例化组件构造器的方式,同时事件监听和销毁逻辑也要换成Vue3的写法。
完整改造后的代码
首先引入Vue3需要的API,然后重写showPrompt函数:
import { createVNode, render } from 'vue' import Modal from '@/layouts/Modal.vue' import { get, set } from 'lodash' export function showPrompt(props: object) { // 合并默认props与传入参数 const propsData = { positiveButtonColor: 'error', showPositiveButton: true, contentSlotPaddingClass: 'pa-0', ...props } // 保留原有的按钮文本判断逻辑 if (get(propsData, 'positiveButtonText', '') === 'Yes') { set(propsData, 'positiveButtonColor', 'primary') } // 创建一个临时DOM容器,用来挂载Modal const container = document.createElement('div') // 处理弹窗关闭后的销毁逻辑 const handleDialogClose = (value: boolean) => { if (!value) { setTimeout(() => { // Vue3中通过render(null)卸载组件,再移除DOM容器 render(null, container) document.body.removeChild(container) }, 1000) } } // 获取用户传入的事件监听 const userListeners = get(propsData, 'listeners', {}) // 创建Modal的VNode,第三个参数传入props和事件监听 const vnode = createVNode(Modal, { ...propsData, // 直接传入isVisible控制显示(如果Modal把isVisible作为prop) isVisible: true, // 合并用户传入的监听和我们的关闭回调 ...userListeners, dialogObserverCallback: handleDialogClose }) // 注入vuetify和i18n依赖,确保Modal能拿到这些全局实例 vnode.appContext = { provide: { vuetify, i18n } } // 渲染VNode到临时容器 render(vnode, container) // 将容器添加到页面body document.body.appendChild(container) // 返回组件实例(Vue3中vnode.component就是实例对象) return vnode.component }
关键改动说明
替换组件实例化方式:
用createVNode创建虚拟节点,再通过render挂载到临时DOM容器,完全替代Vue2里new Klass()的写法。事件监听的替代方案:
不再用$on绑定事件,而是直接在createVNode的第二个参数(props对象)中传入事件回调——Vue3中组件的emit事件可以直接通过这种方式绑定,和模板里的@dialogObserverCallback="xxx"效果一致。销毁逻辑的调整:
Vue3移除了$destroy方法,我们只需要调用render(null, container)就能卸载组件,再手动移除DOM容器即可,避免内存泄漏。全局依赖注入:
原来的new Klass({ vuetify, i18n })是注入依赖,Vue3中通过给vnode.appContext.provide赋值来实现,确保Modal组件能获取到vuetify和i18n实例。
如果Modal的isVisible是内部data的情况
如果你的Modal组件里isVisible是内部的响应式数据(不是prop),那需要在Modal组件里用defineExpose暴露这个属性:
<script setup> import { ref, watch } from 'vue' const emit = defineEmits(['dialogObserverCallback']) const isVisible = ref(false) // 暴露isVisible给外部调用者 defineExpose({ isVisible }) // 监听isVisible变化,触发原有的回调 watch(isVisible, (newVal) => { emit('dialogObserverCallback', newVal) }) </script>
然后在showPrompt函数里,渲染后设置显示:
// 渲染之后设置isVisible render(vnode, container) document.body.appendChild(container) vnode.component?.exposed?.isVisible = true
类型提示优化
如果要解决TypeScript的类型问题,可以引入Vue3的类型定义:
import type { ComponentPublicInstance, VNode } from 'vue' // 调整函数返回类型 export function showPrompt(props: object): ComponentPublicInstance | null { // ... 原有逻辑 return vnode.component }
备注:内容来源于stack exchange,提问作者umut can topal

