如何在Vue 3中编程创建一个组件示例?

fdbelqdn  于 2023-01-17  发布在  Vue.js
关注(0)|答案(4)|浏览(156)

我有一个Vue 2模式,用于常见场景:以编程方式创建一个示例,以便在模板之外的动态内容上打开Modal/Dialog/Lightbox。
在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的完整示例。

z9ju0rcb

z9ju0rcb1#

下面是如何在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.removeChild(mountEl);
        dialog.unmount();
        dialog = null;
      },
    });

    dialog.mount(mountEl);
  },
};

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

2izufjch

2izufjch2#

下面是以编程方式调用和运行组件的简单方法

/* DialogService.js */
import DialogVue from './Dialog.vue';
import { createApp } from 'vue';

const Dialog = (options = {}) => {
  const onClose = options.onClose;
  const tempDiv = document.createElement('div');
  const instance = createApp(DialogVue).mount(tempDiv);

  instance.title = options.title;
  instance.text = options.text;
  instance.onClose = options.onClose;
  instance.show = true;

  document.body.appendChild(instance.$el);
}

export default Dialog;
<!-- Dialog.vue -->
<template>
  <transition name="fade-bottom">
    <h3 v-if="title">{{ title }}</h3>
    {{ text }}
    <button @click="show = false; onClose()">Cancel</button>
  </transition>
</template>

<script setup>
import { ref, defineExpose } from 'vue'

const show = ref(false)
const title = ref('')
const text = ref('')
const onClose = () => {}

defineExpose({
  title,
  text,
  show,
  onClose
})

</script>
qqrboqgw

qqrboqgw3#

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控制其自身的可见性,不需要像在原始代码片段中那样显式卸载。在父卸载时会自动执行清理:

<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,这种情况下可以使用一个可以通过应用传递的事件总线示例进行交互。

ckocjqey

ckocjqey4#

我建议使用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 })

相关问题