Nuxt 2 迁移至 Nuxt 3:自定义模态框插件适配问题求助
Nuxt3 模态框插件迁移问题解决
问题背景
原本基于自定义插件实现了从任意组件通过导入组件打开模态窗口的功能,迁移到 Nuxt3 后遇到两个核心问题:
params.component出现未定义的情况- 使用
createApp创建实例时,无法访问 Pinia 等其他插件
Nuxt2 实现代码
modal.ts
import Vue from 'vue' import {DModal} from '@/components/_base-ts/DModal' Vue.use({ install(Vue) { const modalContainer = document.getElementById('d-modal-main-container') as HTMLElement let destroyModal Object.defineProperty(Vue.prototype, '$modal', { get() { return { show: params => { const instance = new Vue({ parent: this.$root, render: h => h( params.component, { props: params.props, on: {...params.events} } ) }) const mountPoint = modalContainer.appendChild(document.createElement('div')) instance.$mount(mountPoint) destroyModal = () => { instance.$el.remove() instance.$destroy() } }, hide: () => { if (destroyModal) { destroyModal() } } } as DModal } }) } })
模态框调用代码
vm.vnode.context?.$modal.show<string>({ component: () => import('@/components/address-check/AddressCheckModal.vue'), })
Nuxt3 首次尝试代码
import {createApp} from 'vue' import { defineNuxtPlugin } from '#app' import {DModal} from '~/components/_base-ts/DModal' export default defineNuxtPlugin(nuxtApp => { nuxtApp.vueApp.use({ install(Vue) { const modalContainer = document.getElementById('d-modal-main-container') as HTMLElement let destroyModal nuxtApp.provide('modal', { show: (params) => { const instance = createApp( params.component, { props: params.props, on: {...params.events} } ) const mountPoint = modalContainer.appendChild(document.createElement('div')) instance.mount(mountPoint) destroyModal = () => { instance.unmount() // instance.$destroy() } }, hide: () => { if (destroyModal) { destroyModal() } } } as DModal) } }) })
问题解决方案
1. 解决 params.component 未定义问题
Nuxt3 中传入的异步组件(() => import(...))需要先解析完成才能使用,修改 show 方法处理异步逻辑:
async show(params) { // 解析异步组件,拿到实际组件对象 const component = typeof params.component === 'function' ? await params.component() : params.component const resolvedComponent = component.default || component // 后续逻辑使用 resolvedComponent 替代 params.component }
同时调用时确保组件加载完成:
// 组件内调用示例 const { $modal } = useNuxtApp() await $modal.show({ component: () => import('@/components/address-check/AddressCheckModal.vue'), })
2. 解决无法访问 Pinia 等插件的问题
createApp 创建的是独立应用实例,无法继承 Nuxt 上下文。改用 Nuxt 提供的虚拟节点渲染 API,复用 Nuxt 应用上下文:
import { defineNuxtPlugin, createVNode, render } from '#app' import { DModal } from '~/components/_base-ts/DModal' export default defineNuxtPlugin(nuxtApp => { const modalContainer = document.getElementById('d-modal-main-container') as HTMLElement let destroyModal: (() => void) | undefined nuxtApp.provide('modal', { async show(params) { // 解析异步组件 const component = typeof params.component === 'function' ? await params.component() : params.component const resolvedComponent = component.default || component // 创建虚拟节点并绑定 Nuxt 应用上下文 const vnode = createVNode(resolvedComponent, { ...params.props, on: { ...params.events } }) vnode.appContext = nuxtApp.vueApp._context // 创建挂载点并渲染 const mountPoint = document.createElement('div') modalContainer.appendChild(mountPoint) render(vnode, mountPoint) // 销毁逻辑 destroyModal = () => { render(null, mountPoint) mountPoint.remove() } }, hide() { if (destroyModal) { destroyModal() destroyModal = undefined } } } as DModal) })
注意:确保 app.vue 中存在 d-modal-main-container 容器:
<template> <div> <NuxtPage /> <div id="d-modal-main-container"></div> </div> </template>
内容的提问来源于stack exchange,提问作者Slavik Salamandyk
相关产品推荐
相关产品推荐

