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)动态渲染不同的展示组件。

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

相关推荐
前端 贾公子13 分钟前
从入门到实践:前端 Monorepo 工程化实战(4)
前端
菩提小狗16 分钟前
Sqlmap双击运行脚本,双击直接打开。
前端·笔记·安全·web安全
前端工作日常27 分钟前
我学习到的AG-UI的概念
前端
韩师傅32 分钟前
前端开发消亡史:AI也无法掩盖没有设计创造力的真相
前端·人工智能·后端
XiaoYu20021 小时前
第12章 支付宝SDK
前端
双向331 小时前
RAG的下一站:检索增强生成如何重塑企业知识中枢?
前端
拖拉斯旋风1 小时前
从零开始:使用 Ollama 在本地部署开源大模型并集成到 React 应用
前端·javascript·ollama
asing1 小时前
🤯 为什么我的收银台在鸿蒙系统“第一次返回”死活拦不住?一次差点背锅的排查实录
前端·harmonyos
德育处主任1 小时前
『NAS』在群晖部署图片压缩工具-Squoosh
前端·javascript·docker
Hao_Harrision1 小时前
50天50个小项目 (React19 + Tailwindcss V4) ✨| ThreeDBackgroundBoxes(3D背景盒子组件)
前端·3d·typescript·react·tailwindcss·vite7