在 Vue 3 中,
插槽(Slot) 的机制与 Vue 2 类似,但语法和功能有所优化。以下是结合 Vue 3 特性的详细解释:
一、普通插槽:父传子
场景:子组件定义占位符,父组件填充具体内容。
数据流向:父组件 → 子组件
示例:
<!-- 子组件 (ChildComponent.vue) -->
<template><div><slot>默认内容(当父组件未提供时显示)</slot></div>
</template><script setup>
// 子组件无需额外逻辑,仅需定义插槽
</script>
<!-- 父组件 (ParentComponent.vue) -->
<template><ChildComponent><p>父组件传递的内容</p></ChildComponent>
</template><script setup>
import ChildComponent from './ChildComponent.vue';
</script>
- 特点:
- 子组件通过
<slot>
定义占位符。
- 父组件在子组件标签内填充内容,内容会替换子组件的
<slot>
。
- 若父组件未提供内容,子组件的默认内容生效。
二、作用域插槽:子传父
场景:子组件将数据传递给插槽,父组件基于数据自定义渲染。
数据流向:子组件 → 父组件
示例:
<!-- 子组件 (ListComponent.vue) -->
<template><ul><li v-for="item in items" :key="item.id"><!-- 将子组件数据传递给插槽 --><slot :item="item" name="item">默认内容(当父组件未提供时显示)</slot></li></ul>
</template><script setup>
import { ref } from 'vue';const items = ref([{ id: 1, name: "Apple" }]);
</script>
<!-- 父组件 (ParentComponent.vue) -->
<template><ListComponent><!-- 接收子组件传递的数据 --><template #item="{ item }"><span>{{ item.name }}</span></template></ListComponent>
</template><script setup>
import ListComponent from './ListComponent.vue';
</script>
- 特点:
- 子组件通过
v-bind
将数据绑定到插槽(如 :item="item"
)。
- 父组件通过
v-slot
接收数据(如 #item="{ item }"
),并自定义渲染逻辑。
- 支持解构赋值,简化代码。
三、核心区别总结
类型 | 数据流向 | 功能 | Vue 3 语法特性 |
普通插槽 |
父 → 子 |
父组件控制子组件内部内容 |
使用 #default 或省略名称 |
作用域插槽 |
子 → 父 → 子 |
子组件传递数据,父组件决定渲染方式 |
支持解构、动态插槽名(v-slot:[name] ) |
四、常见误区澄清(Vue 3 版本)
1. 插槽本身是父传子
2. 作用域插槽是子传父的数据通道
五、Vue 3 插槽新特性
1. <script setup>
简化语法
<!-- 子组件 -->
<script setup>
const props = defineProps(['items']);
</script><template><ul><li v-for="item in items" :key="item.id"><slot :item="item" /></li></ul>
</template>
2. 动态插槽名
<!-- 父组件 -->
<ChildComponent><template #[dynamicSlotName]="slotProps"><!-- 动态渲染插槽内容 --></template>
</ChildComponent>
3. 具名插槽的简写
<!-- 父组件 -->
<ChildComponent><template #header>头部内容</template><template #footer>底部内容</template>
</ChildComponent>
六、实际应用场景(Vue 3 优化)
1. 列表组件
<!-- 子组件 -->
<template><ul><li v-for="item in items" :key="item.id"><slot :item="item" name="item"><span>{{ item.name }}</span></slot></li></ul>
</template>
<!-- 父组件 -->
<template><ListComponent><template #item="{ item }"><button @click="deleteItem(item.id)">删除</button><span>{{ item.name }}</span></template></ListComponent>
</template>
2. 无渲染组件(逻辑封装)
<!-- 子组件 -->
<script setup>
const { x, y } = useMousePosition();
</script><template><slot :x="x" :y="y" />
</template>
<!-- 父组件 -->
<template><MouseTracker><template #default="{ x, y }">鼠标位置:{{ x }}, {{ y }}</template></MouseTracker>
</template>
七、总结
- 普通插槽:父组件控制子组件内容,适合布局和结构定制。
- 作用域插槽:子组件传递数据,父组件决定渲染逻辑,适合动态内容生成。
- Vue 3 优势:
<script setup>
简化代码、解构赋值提升可读性、动态插槽名增强灵活性。
通过合理使用插槽,可以实现组件的高度复用和灵活定制,同时保持逻辑与 UI 的解耦。