vue插槽
在 Vue 3 中,插槽(Slot) 的机制与 Vue 2 类似,但语法和功能有所优化。以下是结合 Vue 3 特性的详细解释:
1.
一、普通插槽:父传子
场景:子组件定义占位符,父组件填充具体内容。 数据流向:父组件 → 子组件 示例:<!-- 子组件 (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. 插槽本身是父传子
- 正确性:普通插槽的占位符由子组件定义,但内容由父组件填充。
- 示例:
<!-- 父组件 --> <ChildComponent> <p>父内容覆盖子组件插槽</p> </ChildComponent>- 若子组件未定义默认插槽,父组件内容会直接渲染到子组件根节点。
2. 作用域插槽是子传父的数据通道
- 正确性:子组件通过插槽暴露数据,父组件接收后生成内容。
- 示例:
<!-- 子组件 --> <slot :text="greeting"></slot> <!-- 父组件 --> <ChildComponent v-slot="{ text }"> <div>{{ text }}</div> </ChildComponent>- 子组件通过
v-bind传递greeting,父组件通过解构接收。
- 子组件通过
五、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>简化代码、解构赋值提升可读性、动态插槽名增强灵活性。

浙公网安备 33010602011771号