如何在 Vue 3 中以编程方式创建组件实例?

Posted

技术标签:

【中文标题】如何在 Vue 3 中以编程方式创建组件实例?【英文标题】:How to programmatically create a component instance in Vue 3? 【发布时间】:2021-12-12 09:01:18 【问题描述】:

我有一个用于常见场景的 Vue 2 模式:以编程方式创建一个实例以在模板外部的动态内容上打开模态/对话框/灯箱。

在 Vue 2 中,我发现了这种模式:

// DialogService.js

export default 
  alert(text) 
    const DialogClass = Vue.extend(DialogComponentDef);
    let dialog = new DialogClass( propsData:  text  );

    dialog.$on('close', () => 
      dialog.$destroy();
      dialog.$el.remove();
      dialog = null;
    );

    // mount the dynamic dialog component in the page
    const mountEl = document.createElement('div');
    document.body.appendChild(mountEl);
    dialog.$mount(mountEl);
  ,
;

如果知道Vue.extends$on$destroy 不再存在,我该如何在 Vue 3 中实现这一点? 您可以通过clicking here 看到 DialogService.js 的完整示例。

【问题讨论】:

不是重复的而是相关的,***.com/questions/63471824/vue-js-3-event-bus 其实一点关系都没有。事件总线不是这里的问题。 是的。您创建可以以编程方式使用的事件总线(on 和 emit 方法)。 Vue 3 没有提供这样的总线,所以需要外部提供。其余的可能是相同的,或多或少。 'new Vue' 被'createApp' 取代。不要把它看作是扩展组合,而是子应用程序,因为它确实是一个 好吧,createApp 不会保留前一个应用程序的上下文,而 Vue.extend 会保留,所以 createApp 无论如何都不是解决方案。我更改了标题,使其更加明确。 【参考方案1】:

Vue 3 不提供通用事件总线。它可以替换为轻量级的第三方替代品,例如 mitteventemitter3

可以使用teleport 将组件安装在应用程序元素层次结构之外。之前在 Vue 2 中可以使用第三方 portal-vue 库。模态框和其他屏幕 UI 元素是它的常见用例

<teleport to="body">
  <DialogComponent ref="dialog" @close="console.log('just a notification')">
   Some markup that cannot be easily passed as dialog.value.show('text')
  </DialogComponent>
</teleport>

DialogComponent 控制自己的可见性,不需要像原来的 sn-p 那样显式卸载。在父卸载时自动执行清理:

<teleport to="body">
  <div v-if="dialogState">
    <slot>dialogText</slot>
  </div>
</teleport>

let dialogState = ref(false);
let dialogText = ref('');
let show = (text) => 
  dialogText.value = text;
  dialogState.value = true;
 ;
...
return  show ;

对于需要管理多个实例或在业务逻辑中访问show 外部组件的更复杂的场景,需要在组件层次结构的顶部安装一个teleport。在这种情况下,可以通过应用程序传递的事件总线实例可用于交互。

【讨论】:

好吧,我的问题与事件总线无关。另外,这里的模态只是模式的一个例子,这里的整个模式是当你不在模板中时能够实例化一个组件。 Teleport 旨在解决必须出现在模板 B 中的模板 A 中完成的事情。这与这里的场景不同。 好吧,那它的措辞可能会更好。这是执行您在 V3 中询问的常见方法。 V2 没有传送装置来在应用程序外部安装组合,因此您必须通过手动安装来跳过箍。 我更改了标题,使其更加明确。【参考方案2】:

这里是如何在 Vue 3 中使用 createApp,但不会保留上下文(存储、插件、指令...)。

// DialogService.js
import  createApp  from 'vue';

export default 
  alert(text) 
    const mountEl = document.createElement('div');
    document.body.appendChild(mountEl);

    const dialog = createApp( extends: DialogComponentDef , 
      // props
      text,
      // events are passed as props here with on[EventName]
      onClose() 
        mountEl.parentNode.remvoeChild(mountEl);
        dialog.unmount();
        dialog = null;
      ,
    );

    dialog.mount(mountEl);
  ,
;

为了保持上下文,这里可以使用hrender Vue 方法看到更复杂的内容:https://github.com/vuejs/vue-next/issues/2097#issuecomment-709860132

【讨论】:

【参考方案3】:

我建议使用mount-vue-component。它重量轻且易于使用。代码示例:

import MyComponent1 from './MyComponent1.vue'
import MyComponent2 from './MyComponent2.vue'
import  mount  from 'mount-vue-component'

let dynamicComponent = mount(someCondition ? MyComponent1 : MyComponent2,  props:  <someProperties...> , app: MyVueApp )

【讨论】:

以上是关于如何在 Vue 3 中以编程方式创建组件实例?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Java 中以编程方式启动和停止 Amazon EC2 实例

如何以编程方式创建 Vue.js 插槽?

如何获取在 Xamarin MacOS 中以编程方式创建的 NSTextField 的值?

如何在 Nativescript-vue 中以编程方式折叠 raddataform“组”

Vue3在按钮单击时以编程方式创建组件实例

Vue.js:以编程方式实例化功能组件