Vue3-动态组件

在 Vue 3 中,动态组件的核心是使用内置的 <component> 元素,并通过其 :is prop 来指定要渲染的组件。动态组件允许在同一个挂载点(<component> 标签)动态地切换和渲染不同的组件。

1.基本写法

  1. 导入你想要动态切换的组件。
  2. 创建一个 refshallowRef (推荐) 来持有当前要渲染的组件对象本身
  3. 在模板中,将这个 ref 绑定到 <component :is="...">
ts 复制代码
<template>
  <div>
    <button @click="switchComponent('ComponentA')">显示组件 A</button>
    <button @click="switchComponent('ComponentB')">显示组件 B</button>

    <hr>

    <component :is="currentComponent" />
  </div>
</template>

<script setup lang="ts">
import { ref, shallowRef, markRaw, type Component } from 'vue';

// 1.导入你的组件
import ComponentA from './ComponentA.vue';
import ComponentB from './ComponentB.vue';

// 2. 定义一个对象来映射组件名称或键
// 使用 markRaw 告诉 Vue 这些组件不需要被深度响应式处理,可以优化性能
const componentsMap: Record<string, Component> = {
  ComponentA: markRaw(ComponentA),
  ComponentB: markRaw(ComponentB)
};

// 3. 创建一个 ref 来存储当前要渲染的组件对象
// 使用 shallowRef 是最佳实践,因为组件对象本身不需要深度响应
// 并使用 Component 类型,它从 'vue' 导入
const currentComponent = shallowRef<Component>(componentsMap.ComponentA);

// 4. 定义切换组件的方法
const switchComponent = (componentName: string) => {
  if (componentsMap[componentName]) {
    currentComponent.value = componentsMap[componentName];
  }
}
</script>

关键点:

  • shallowRef : 当处理像组件这样的大型、不可变对象时,使用 shallowRefref 更高效,因为它只使 .value 的赋值具有响应性,而不会尝试深度代理组件对象内部。
  • markRaw: 这是一个性能优化。它明确告诉 Vue 跳过对这些组件对象的响应式代理。
  • type Component : 这是从 vue 导入的通用组件类型,非常适合用在此处。

2.结合 <KeepAlive> 保持状态

当你切换动态组件时,默认情况下,前一个组件实例会被销毁。如果你希望保留组件的状态(比如表单中已填写的数据),你可以使用 <KeepAlive> 将其包裹起来。

ts 复制代码
<template>
  <div>
    <button @click="switchComponent('ComponentA')">显示组件 A</button>
    <button @click="switchComponent('ComponentB')">显示组件 B</button>

    <hr>

    <KeepAlive>
      <component :is="currentComponent" />
    </KeepAlive>
  </div>
</template>
 
<script setup lang="ts">
// ... 其余 script 部分同上
import { shallowRef, markRaw, type Component } from 'vue';
import ComponentA from './ComponentA.vue';
import ComponentB from './ComponentB.vue';

const componentsMap: Record<string, Component> = {
  ComponentA: markRaw(ComponentA),
  ComponentB: markRaw(ComponentB)
};

const currentComponent = shallowRef<Component>(componentsMap.ComponentA);

const switchComponent = (componentName: string) => {
  if (componentsMap[componentName]) {
    currentComponent.value = componentsMap[componentName];
  }
}
</script>

3.传递 Props 和监听事件

你可以像对待普通组件一样,向 <component> 元素传递 props 和监听事件。这些 props 和事件监听器会"穿透"并应用到 :is 所渲染的实际组件上。

ts 复制代码
<template>
  <KeepAlive>
    <component 
      :is="currentComponent"
      :message="dynamicMessage"
      @some-event="handleEvent" 
    />
  </KeepAlive>
</template>

<script setup lang="ts">
import { ref, shallowRef, markRaw, type Component, computed } from 'vue';
import ComponentA from './ComponentA.vue';
import ComponentB from './ComponentB.vue';

// ... 组件映射和 currentComponent 同上 ...
const componentsMap: Record<string, Component> = { /* ... */ };
const currentComponent = shallowRef<Component>(componentsMap.ComponentA);
const switchComponent = (componentName: string) => { /* ... */ }

// 1. 准备要传递的 props
const dynamicMessage = ref<string>('这是动态消息');

// 2. 准备事件处理函数
const handleEvent = (payload: any) => {
  console.log('动态组件触发了事件:', payload);
}

</script>

注意 :如果 ComponentAComponentB 接受的 props 不同,可能需要根据currentComponent的值来动态计算要传递的 props 对象。

4.常见使用场景

动态组件在需要灵活切换UI视图的场景中非常有用:

  1. 标签页

    • 这是最经典的用法。你有一个标签栏,点击不同的标签,下方的内容区域就渲染对应的组件,同时使用 <KeepAlive> 缓存已加载的标签页内容。
  2. 多步骤表单/向导

    • 在一个多步骤的注册流程或配置向导中,"上一步" 和 "下一步" 按钮只是切换 <component :is="..."> 绑定的组件(Step1.vue, Step2.vue ...),而无需切换路由。
  3. 仪表盘小部件

    • 允许用户自定义他们的仪表盘,从一个列表中选择不同的小部件(如"天气组件"、"图表组件"、"待办事项组件")。这些小部件都可以在同一个插槽中通过动态组件来渲染。
  4. 根据数据类型显示不同视图

    • 比如你有一个列表,列表项的类型不同(例如,文章、视频、广告),你可以根据列表项的类型(item.type)动态渲染不同的展示组件。

总之,当你需要在一块区域根据条件或用户交互来"替换"整个组件时,动态组件就是你的首选方案。

相关推荐
单身的人上天堂4 小时前
开发中使用iconfont预览太麻烦?我开发了一款VSCode插件来提升效率
前端·javascript·visual studio code
鹏多多4 小时前
前端项目package.json与package-lock.json的详细指南
前端·vue.js·react.js
敲代码的独角兽4 小时前
一文搞懂JavaScript事件循环 (Event Loop)
前端
yujunlong39194 小时前
Redux Toolkit (RTK) + TypeScript
前端·typescript·react
AI视觉网奇4 小时前
live2d 单图转模型 单图生成模型
java·前端·python
weixin_395448914 小时前
“一次性拼接 RM+FSD 做单次前向/反向”的方案
前端·javascript·推荐算法
一只爱吃糖的小羊4 小时前
深入 React 原理:Reconciliation
前端·javascript·react.js
哆啦A梦15884 小时前
商城后台管理系统 03 Vue项目-实现表格导出EXCEL表格
前端·vue.js·excel
程序员爱钓鱼4 小时前
BlackHole 2ch:macOS无杂音录屏与系统音频采集完整技术指南
前端·后端·设计模式