动态渲染的时候经常会纠结用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>