当前位置: 首页 > 知识库问答 >
问题:

javascript - vue3 如何才能多次调用 createApp?

贺雅健
2024-09-03

vue3 中说 createApp 只能调用一次,可是我现在有两个场景需要手动挂载 组件的情况:

场景一

手动挂载弹窗,比如我手动挂载一个按钮组件:

const SubVue = Vue.extend(Button)
const ButtonInstance = new SubVue({
  propsData: {
    type: 'primary'
  },
})
ButtonInstance.$slots = {
  default: 'el-button',
}
ButtonInstance.$mount(this.$refs.container) 

vue3 中不再支持 extend, 因此我使用createApp代替,可是第二次调用createApp的返回值和第一次调用的返回值不同,第二次的返回值没有umount方法,挂载后无法手动卸载应用。

场景二

有你一个表格,我使用render函数自定义列内容,比如:

    {
      label: '调查情况',
      // prop: 'iis',
      prop: ({ row }) => {
        const item = planOptions.find(item => item.value === row.iis)
        return <span>{item?.label ?? ''}</span>
      },
    },

prop 传递一个 render 函数,实现列自定义。

前端在导出当前页数据的时候,为了能拿到 render 函数渲染后的数据,我就手动调用 careateApp 手动渲染 vNode, 然后获取 textContent, 在使用 textContent 填充表格,去导出,目的是为了拿到映射后的结果,用户能理解。

关键代码:

const vNode = item.prop({ row })
console.log(vNode)
// eslint-disable-next-line vue/one-component-per-file
const rowApp = createApp({
  render() {
    return h(vNode)
  },
}).mount('#temp-row')
value = el.textContent
rowObj[item.label] = value
console.log(rowApp)
// rowApp.unmount() // rowApp 没有 umount 方法,无法卸载
为何我手动挂载 render 函数?

我想要拿到最后渲染输出,再导出到 excel 表格中,比如 接口返回 1 表示成功,表格的某一列需要显示成功,为了映射关系因包含在表格的自定义列的 render 函数里,我手动得挂载 render, 再去获取渲染后的 textContent , 就是用户能理解的数据,而不是 1.

rowApp 没有 umount 方法,无法卸载这个零时的 app, 使用 vue 的开发工具查看,能看到很多 App。

QQ_1725359152174.png

问题总结,一个 vue3 应用中无法多次调用createApp,比如这样:

const app: App = createApp(App) // 一个任意组件

console.log('app')
console.log(app)

const rowApp = createApp({
  setup(){
      return ()=>h('p','row-app')
  }
}).mount('#temp-row')

console.log('rowApp')
console.log(rowApp)

两次调用的返回值不同,第二次调用无法卸载 rowApp。

如何多次调用 createApp 或者如何变相实现我这两个需求?

共有1个答案

秦博达
2024-09-03

在 Vue 3 中,createApp 函数确实可以多次调用以创建多个 Vue 应用实例,但每个实例都是独立的,并且每个实例都有自己的生命周期和挂载点。关于你提到的两个问题,我们可以分别探讨解决方案。

场景一:手动挂载和卸载组件

在 Vue 3 中,由于不再支持 Vue.extend$mount/$destroy 这样的实例方法,你需要通过不同的方式来手动挂载和卸载组件。一个常见的方法是使用 mountunmountComponentAtVNode(如果你使用的是 Vue 3 的 Composition API 插件,如 @vue/composition-api 在 Vue 2 中,或者类似的第三方库)。但在纯 Vue 3 中,你可以直接操作 DOM 来卸载组件。

这里是一个简单的示例,展示如何挂载和卸载一个组件:

// 创建一个根组件
const AppComponent = {
  setup() {
    return () => h('button', { type: 'primary' }, 'Click me');
  }
};

// 挂载组件
const mountNode = document.getElementById('app');
const app = createApp(AppComponent).mount(mountNode);

// 卸载组件
function unmountComponent() {
  // 直接移除 DOM 节点
  mountNode.innerHTML = '';
  // 如果需要,可以调用 app.unmount()(如果 Vue 3 提供了这样的方法,但在纯 Vue 3 中通常不这样做)
  // 注意:Vue 3 默认没有 app.unmount(),但可以通过其他方式实现类似的功能
}

// 稍后,你可以调用 unmountComponent() 来卸载组件

注意:Vue 3 并没有直接提供一个 unmount 方法来卸载整个应用实例。通常,你可以通过移除挂载点(如上例所示)来卸载组件。

场景二:在导出数据时渲染 JSX/VNode

在你的场景二中,你试图通过 createApp 来渲染 JSX/VNode 并获取其文本内容。然而,这种方法并不是最高效或最直接的。你可以直接在 JavaScript 中渲染 JSX/VNode 并使用 DOM API 来获取文本内容,而无需挂载到 DOM 上。

例如,使用 renderToString(如果你在使用 Vue 3 的服务器端渲染功能或类似的库)或者直接使用 h 函数和 DOM 操作:

import { h, renderToString } from 'vue';

// 假设 item.prop 是一个返回 VNode 的函数
const vNode = item.prop({ row });

// 如果你只是想获取渲染后的字符串(对于 SSR),可以使用 renderToString
// 注意:这需要你在服务器端或支持 SSR 的环境中
// const htmlString = renderToString(vNode);

// 如果你只是想获取文本内容,可以创建一个临时的 DOM 元素并渲染 VNode
const tempDiv = document.createElement('div');
render(vNode, tempDiv); // 注意:这里使用的是 render 而不是 createApp.mount
const textContent = tempDiv.textContent;

// 使用 textContent ...
// 然后清理 DOM
tempDiv.remove();

注意:在这个示例中,我使用了 render 函数而不是 createApp.mountrender 函数用于将 VNode 渲染到 DOM 节点上,但它不会创建一个完整的 Vue 应用实例。这通常用于渲染静态内容或小型组件,而不需要 Vue 的响应式系统或生命周期钩子。

总结来说,Vue 3 允许你多次调用 createApp,但每个应用实例都是独立的,并且你可能需要手动管理它们的挂载和卸载(通过操作 DOM 或其他方式)。对于 JSX/VNode 的渲染,考虑使用 render 函数而不是 createApp.mount,除非你确实需要一个完整的 Vue 应用实例。

 类似资料:
  • 问题内容: 我正在使用以下代码通过AJAX提交表单: 背景 我的PHP处理程序执行各种任务,然后发回响应。然后,我可以在成功或错误函数中执行某些操作。 我的问题 当用户双击表单的“提交”按钮时,将发生两次AJAX调用,这将导致我的PHP处理程序中的代码执行两次。 我的问题 如果用户双击提交,如何避免我的代码执行两次? 问题答案: 当AJAX调用再次出现时,请先禁用首次单击的“提交”按钮,然后重新启

  • 最近发现vue3在在mode为function和module下有时候编译的结果不一样。 于是想调试一下vue3的编译过程,特别是插值表达式中变量被替换为诸如ctx.aa这种的过程。 折腾了好久,发现只能调试mode为function时候的过程,在vite或者vue-cli项目中调试mode为module,但是到模版的编译时候,只能看见openBlock函数和setupBlock函数,但是看不见re

  • 问题内容: 我需要创建一个只能执行一次的函数,在第一次执行后,每次都不会执行。我从C++和Java知道可以完成此工作的静态变量,但我想知道是否有更优雅的方法来做到这一点? 问题答案: 如果用“将不执行”来表示“多次调用将不执行任何操作”,则可以创建一个闭包: 使用全局变量,其他代码可以重置“已执行”标志的值(无论您为它选择什么名称)。使用闭包时,其他代码都无法做到这一点,无论是偶然还是故意的。 正

  • Nietschze夸大了他所说的: 杀不死我们的,只会让我们更强大。 你最大的责任是对你的团队负责。你应该非常了解他们中的每个人。你应该激励你的团队,但不要让他们过劳。你通常应该告诉他们他们被激励的方式。如果他们觉得划算,他们会被很好的激励。每个工程中,或者在每个其他的工程里,试着同时用他们建议的以及你认为对他们好的方式去激励他们。激励他们的方法不是给他们更多工作,而是给他们一个新的技能或在团队里

  • Ive尝试了所有的方法,尽可能多地查看文档,但似乎找不到解决方案。

  • 我有一个场景,一个特定的日志消息可能会被打印很多次(可能是数百万次)。例如,如果我们记录(使用方法)每个缺少字段的记录,那么我们可能会记录很多--在输入文件有很多缺少字段的记录的情况下(例如,HDFS上的大文件)。这会很快填满磁盘空间。 为了避免这种情况,我正在尝试为每1000个缺少字段的记录(例如)记录一次。我可以在log4j包之外实现所有这些逻辑,但我想知道是否有更干净的方法来实现这些。理想情

  • 我的主要活动是装载不同的碎片。此外,可以从MainActivity打开设置-activity。 如果用户只是在片段之间切换,那就万事大吉了。 当打开设置-activity并返回MainActivity时,onResume和onPause会被调用两次。如果用户打开设置-activity并再次返回MainActivity,onResume和onPause将被调用三次。每次用户打开“设置-activit