动态渲染子组件

187 阅读2分钟

动态渲染的时候经常会纠结用slot还是<component :is=""/>,稍微记录总结。

1. 插槽(Slot)

适用场景:

  • 内容分发:当父组件需要向子组件传递模板内容(HTML 或组件)时,插槽是更自然的选择。
  • 布局定制:子组件提供结构框架(如布局容器、卡片样式等),父组件控制内部内容。
  • 默认内容:插槽可以设置默认内容,父组件不传内容时会显示默认值。
  • 作用域插槽:父组件需要访问子组件内部的数据(如循环中的每一项数据)。

示例:

<!-- 子组件 (Child.vue) -->
<template>
  <div class="card">
    <slot name="header">默认标题</slot>
    <slot :data="childData"></slot>
  </div>
</template>

<!-- 父组件 -->
<Child>
  <template #header>自定义标题</template>
  <template #default="{ data }">{{ data }}</template>
</Child>

优点:

  • 父子组件解耦,子组件只负责容器,内容由父组件控制。
  • 支持更灵活的模板结构(如具名插槽、作用域插槽)。

2. 动态组件(<component :is>

适用场景:

  • 动态切换组件:需要根据条件(如路由、用户输入等)切换不同的完整组件
  • 运行时决定组件类型:比如根据数据渲染不同的表单控件(输入框、下拉框等)。
  • 异步加载组件:配合 defineAsyncComponent 实现按需加载。

示例:

<template>
  <component :is="currentComponent" :prop="value" />
</template>

<script>
import ComponentA from './ComponentA.vue';
import ComponentB from './ComponentB.vue';

export default {
  data() {
    return {
      currentComponent: 'ComponentA',
    };
  },
  components: { ComponentA, ComponentB },
};
</script>

优点:

  • 适合完全不同的组件切换(组件结构可能完全不同)。
  • 可以通过 v-bind 传递动态 props。

优化: 多个tabs切换的时候不需要一个个引用子组件

import ComponentA from './ComponentA.vue';
import ComponentB from './ComponentB.vue';
import ComponentB from './ComponentC.vue';
import ComponentB from './ComponentD.vue';
import ComponentB from './ComponentE.vue';
components: { ComponentA, ComponentB, ComponentC, ComponentD, ComponentE }, 

可以用动态切换

const list = ref([
  {
    type: 'ComponentA',
    name: 'tabsA',
  },
  {
    type: 'ComponentB',
    name: 'tabsB',
  },
  {
    type: 'ComponentC',
    name: 'tabsC',
  },
  {
    type: 'ComponentD',
    name: 'tabsD',
  },
  {
    type: 'ComponentE',
    name: 'tabsE',
  }])
  
// 切换
const modules = import.meta.glob('./*.vue'); 
const changeTab = (type) => {
  currentComponent.value = defineAsyncComponent(() => modules[`./${type}.vue`]());
};

modules的作用是匹配当前目录所有 .vue 文件

关键区别

特性Slot(插槽)<component :is>(动态组件)
用途内容分发(父→子传递模板片段)动态切换整个组件
灵活性更高(支持作用域、具名插槽)较低(直接替换组件)
性能更轻量(仅渲染内容)可能触发组件销毁/重建(需优化)
适用层级父子组件间任意组件间
典型场景布局容器、可复用的UI框架标签页、动态表单、路由视图

如何选择?

  • 用 Slot 如果
    你需要父组件控制子组件的部分内容(如标题、按钮、列表项等),且子组件是一个固定的容器。
  • 用 <component :is> 如果
    你需要根据条件切换整个组件(如不同页面、完全不同的功能模块)。

组合使用

两者并不互斥,可以结合使用。例如:

  • 在动态组件内部使用插槽,实现更灵活的定制。
  • 通过插槽传递动态组件(父组件决定子组件渲染什么动态内容)。
<!-- 父组件 -->
<Child>
  <template #content>
    <component :is="dynamicComponent" />
  </template>
</Child>