Vue 3 实现函数调用组件的完整指南

1,391 阅读4分钟

公众号:暮北林
Q Q 群 : 一起学前端

Vue 3 实现函数调用组件的完整指南

在 Vue 3 中,有时我们需要动态创建和挂载组件,例如实现一个弹窗、通知或某些特定的交互组件。与直接在模板中定义组件不同,函数调用组件可以动态创建实例,同时便于灵活传参和事件处理。

本文将详细介绍如何使用 Vue3的createApp创建一个支持 props 和事件处理的函数调用组件,并提供两种不同的实现方式:templaterender


什么是函数调用组件?

函数调用组件是一种通过 JavaScript 函数动态创建 Vue 组件实例的实现方式。它的常见场景包括:

  • 弹窗(如 MessageBox
  • 通知组件(如 Toast
  • 特定交互(如动态表单、选择器)

其核心特点是:

  • 动态创建:不需要预定义在模板中。
  • 灵活传参:支持动态传递 props 和事件处理。
  • 销毁机制:组件可以在合适的时机被清理。

基础实现

我们将以一个名为 ChanceReassign 的组件为例,展示如何实现函数调用组件。组件支持 propsemit 事件。

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({
    inheritAttrsfalse// 禁用默认属性继承
    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;
}

核心逻辑说明

  1. 动态容器创建:通过 document.createElement 创建一个 DOM 容器,供组件挂载。
  2. setup** 方法中解构 ****attrs**:将事件绑定 (onConfirmonClose 等) 通过 v-on="attrs" 动态传递给组件。
  3. 插件注册与全局组件:通过 VantInstall 或其他方式注册全局依赖,确保组件功能完整。
  4. 提供销毁机制:动态创建的容器需在组件卸载时清理,防止内存泄漏。

使用示例

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;
}

核心逻辑说明

  1. render** 函数**:通过 h 函数动态创建虚拟节点(VNode),支持动态绑定 propsattrs
  2. 事件过滤:通过 props 中以 on 开头的键动态绑定事件。
  3. 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

总结

通过 createApptemplaterender,我们可以轻松实现函数调用组件。根据场景选择适合的实现方式,可以帮助我们构建更加灵活、高效的 Vue 应用。

如果你有类似的需求,不妨尝试一下!