Vue 3 实现函数调用组件的完整指南
在 Vue 3 中,有时我们需要动态创建和挂载组件,例如实现一个弹窗、通知或某些特定的交互组件。与直接在模板中定义组件不同,函数调用组件可以动态创建实例,同时便于灵活传参和事件处理。
本文将详细介绍如何使用 Vue3的createApp创建一个支持 props 和事件处理的函数调用组件,并提供两种不同的实现方式:template 和 render。
什么是函数调用组件?
函数调用组件是一种通过 JavaScript 函数动态创建 Vue 组件实例的实现方式。它的常见场景包括:
- 弹窗(如
MessageBox) - 通知组件(如
Toast) - 特定交互(如动态表单、选择器)
其核心特点是:
- 动态创建:不需要预定义在模板中。
- 灵活传参:支持动态传递
props和事件处理。 - 销毁机制:组件可以在合适的时机被清理。
基础实现
我们将以一个名为 ChanceReassign 的组件为例,展示如何实现函数调用组件。组件支持 props 和 emit 事件。
1. 基础的 template 实现
在 Vue 3 中,createApp 是动态创建组件实例的核心工具。我们首先使用 template 方式:
import ChanceReassign from '@/components/chanceReassign/chanceReassign.vue';
import { createApp } from 'vue';
import VantInstall from '@/plugins/vantInstall'; // 假设你有一个 Vant 插件注册逻辑
export default function chanceReturn(props) {
// 创建动态容器
const container = document.createElement('div');
container.classList.add('SHARE');
document.body.appendChild(container);
// 创建 Vue 应用实例
const app = createApp({
inheritAttrs: false, // 禁用默认属性继承
setup(_, { attrs }) {
return { props, attrs }; // 将 props 和 attrs 暴露给模板
},
template: `
<ChanceReassign vv-bind="props" v-on="attrs" <!-- 动态传递组件属性和事件:props 包含传递给组件的所有参数,而 attrs 捕获了所有事件监听器的绑定。通过这样的绑定,组件既可以接收动态属性,也能正确触发父组件传递的事件。 -->/>
`,
});
// 注册全局插件和组件
VantInstall(app);
app.component('ChanceReassign', ChanceReassign);
// 挂载到容器
const instance = app.mount(container);
// 提供销毁方法
instance.unmountApp = () => {
app.unmount();
document.body.removeChild(container);
};
return instance;
}
核心逻辑说明
- 动态容器创建:通过
document.createElement创建一个 DOM 容器,供组件挂载。 setup** 方法中解构 ****attrs**:将事件绑定 (onConfirm、onClose等) 通过v-on="attrs"动态传递给组件。- 插件注册与全局组件:通过
VantInstall或其他方式注册全局依赖,确保组件功能完整。 - 提供销毁机制:动态创建的容器需在组件卸载时清理,防止内存泄漏。
使用示例
import chanceReturn from '@/utils/chanceReturn';
const instance = chanceReturn({
someProp: 'value',
onConfirm: () => console.log('Confirmed!'),
onClose: () => console.log('Closed!'),
});
// 销毁组件
setTimeout(() => {
instance.unmountApp();
}, 5000);
2. 基于 render 的实现
render 函数比 template 更加灵活,适合动态处理逻辑更复杂的场景,例如插入 slots 或动态子组件。
import { createApp, h } from 'vue';
import ChanceReassign from '@/components/chanceReassign/chanceReassign.vue';
import VantInstall from '@/plugins/vantInstall'; // 假设你有一个 Vant 插件注册函数
export default function chanceReturn(props) {
// 创建容器
const container = document.createElement('div');
container.classList.add('SHARE');
document.body.appendChild(container);
// 创建 Vue 应用实例
const app = createApp({
render() {
// 使用 h 函数创建 VNode
return h(ChanceReassign, {
...props, // 传递 props
// 事件绑定,确保所有事件可以传递
...Object.fromEntries(
Object.entries(props).filter(([key]) => key.startsWith('on'))
),
});
},
});
// 注册全局插件和组件
VantInstall(app);
app.component('ChanceReassign', ChanceReassign);
// 挂载到容器
const instance = app.mount(container);
// 提供销毁方法
instance.unmountApp = () => {
app.unmount();
document.body.removeChild(container);
};
return instance;
}
核心逻辑说明
render** 函数**:通过h函数动态创建虚拟节点(VNode),支持动态绑定props和attrs。- 事件过滤:通过
props中以on开头的键动态绑定事件。 - 与
template的区别:render提供更灵活的逻辑控制,比如动态插入slots。
使用方式
与 template 实现类似:
const instance = chanceReturn({
someProp: 'value',
onConfirm: () => console.log('Confirmed!'),
});
instance.unmountApp();
两种方式对比
| 特性 | template 实现 | render 实现 |
|---|---|---|
| 简洁性 | 模板语法简单直观 | JavaScript 逻辑稍复杂 |
| 动态性 | 支持动态 props 和事件绑定 | 支持动态 props、事件和复杂逻辑 |
插入 slots 支持 | 较难实现 | 易于实现 |
| 扩展性 | 中等 | 高 |
适用场景
- 如果只是传递简单的
props和事件,template更易读。 - 如果需要动态插入
slots或实现复杂逻辑,建议使用render。
总结
通过 createApp 和 template 或 render,我们可以轻松实现函数调用组件。根据场景选择适合的实现方式,可以帮助我们构建更加灵活、高效的 Vue 应用。
如果你有类似的需求,不妨尝试一下!