如何以编程方式在 Vue 3 中创建组件实例?
How to programmatically create a component instance in Vue 3?
我有一个用于常见场景的 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 的完整示例
Vue 3 不提供通用事件总线。它可以替换为轻量级的第三方替代品,如 mitt
或 eventemitter3
.
可以使用 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
外部组件的更复杂的场景,传送需要安装在组件层次结构的顶部。在这种情况下,可以使用可以通过应用程序传递的事件总线实例进行交互。
以下是如何在 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);
},
};
为了保持上下文,这里可以看到一些更复杂的东西 h
和 render
Vue 方法:https://github.com/vuejs/vue-next/issues/2097#issuecomment-709860132
我建议使用 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 })
这是以编程方式调用和 运行 组件的简单方法
/* 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>
我有一个用于常见场景的 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.
Vue 3 不提供通用事件总线。它可以替换为轻量级的第三方替代品,如 mitt
或 eventemitter3
.
可以使用 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
外部组件的更复杂的场景,传送需要安装在组件层次结构的顶部。在这种情况下,可以使用可以通过应用程序传递的事件总线实例进行交互。
以下是如何在 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);
},
};
为了保持上下文,这里可以看到一些更复杂的东西 h
和 render
Vue 方法:https://github.com/vuejs/vue-next/issues/2097#issuecomment-709860132
我建议使用 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 })
这是以编程方式调用和 运行 组件的简单方法
/* 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>