You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Nuxt 2 迁移至 Nuxt 3:自定义模态框插件适配问题求助

Nuxt3 模态框插件迁移问题解决

问题背景

原本基于自定义插件实现了从任意组件通过导入组件打开模态窗口的功能,迁移到 Nuxt3 后遇到两个核心问题:

  • params.component 出现未定义的情况
  • 使用 createApp 创建实例时,无法访问 Pinia 等其他插件

Nuxt2 实现代码

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.03 14:01:17