Vue3中如何用函数渲染弹窗?正确实现可传组件的useModal组合式函数
Vue3 函数式弹窗实现及上下文访问问题解决
一、函数渲染弹窗的基本思路
Vue3中通过函数渲染弹窗,核心是利用h()创建虚拟节点,再通过render()将其挂载到动态生成的DOM容器中,最后添加到页面body;关闭时销毁渲染并移除容器。
二、上下文访问问题的原因
你的现有代码中,弹窗内容组件是渲染在独立的DOM容器中,没有继承当前应用的上下文(如router、store这类通过provide/inject传递的实例),因此无法访问这些全局依赖。
三、修正后的useModal实现
// composables/useModal.ts import { h, render, ref, type Ref, type Component, getCurrentInstance } from 'vue'; import AppModal from '@/components/App/Modal/AppModal.vue'; interface ModalOptions { content: string | Component; positionBody?: 'center' | 'bottom'; isCloseButton?: boolean; } export const useModal = () => { // 将实例容器改为局部ref,避免多个useModal调用冲突 const modalInstance: Ref<null | HTMLElement> = ref(null); // 获取当前组件实例的应用上下文 const instance = getCurrentInstance(); const closeModal = () => { if (modalInstance.value) { render(null, modalInstance.value); document.body.removeChild(modalInstance.value); modalInstance.value = null; } }; const modal = (options: ModalOptions) => { closeModal(); const container = document.createElement('div'); document.body.appendChild(container); const modalSlots = { content: () => { if (typeof options.content === 'string') { return options.content; } // 创建内容组件的虚拟节点,并绑定当前应用上下文 const contentVNode = h(options.content as Component); if (instance?.appContext) { contentVNode.appContext = instance.appContext; } return contentVNode; } }; const modalContent = h(AppModal, { show: true, positionBody: options.positionBody, isCloseButton: options.isCloseButton, onClose: closeModal, }, modalSlots); render(modalContent, container); modalInstance.value = container; }; return { modal, closeModal }; };
四、关键修改点说明
- 局部容器引用:将
modalInstance从全局移到useModal函数内部,确保每个组件调用useModal时拥有独立的弹窗实例,避免相互干扰。 - 上下文传递:通过
getCurrentInstance()获取当前组件的实例,将其appContext赋值给内容组件的虚拟节点,让弹窗内的组件能继承应用级的provide内容(如$router、$store)。 - 插槽逻辑优化:统一插槽的返回逻辑,确保内容组件能正确绑定上下文。
五、使用方式(和原代码一致)
<script setup lang="ts"> import { useModal } from '@/composables/useModal'; import MyComponent from '@/components/MyComponent.vue'; const { modal } = useModal(); modal({ content: MyComponent }); </script>
内容的提问来源于stack exchange,提问作者VTsM
相关产品推荐
相关产品推荐

