第 8 章:插槽
学习目标
- 区分默认插槽、具名插槽、作用域插槽
- 用
useSlots/useAttrs操作插槽 - 实现可复用布局组件
- 避开插槽封装的 3 个坑
一、默认插槽
vue
<!-- Card.vue -->
<template>
<div class="card">
<slot>默认内容</slot>
</div>
</template>vue
<Card>
<p>自定义内容</p>
</Card>二、具名插槽
vue
<!-- Layout.vue -->
<template>
<div class="layout">
<header><slot name="header" /></header>
<main><slot /></main> <!-- name 默认是 default -->
<footer><slot name="footer" /></footer>
</div>
</template>vue
<Layout>
<template #header>
<h1>标题</h1>
</template>
<p>主体内容</p>
<template #footer>
<small>© 2026</small>
</template>
</Layout>⚠️ 坑 1:
#header是v-slot:header的简写,只能用在<template>或组件上。
三、作用域插槽(子提供数据)
vue
<!-- List.vue -->
<script setup lang="ts">
defineProps<{ items: { id: number; name: string }[] }>()
</script>
<template>
<ul>
<li v-for="item in items" :key="item.id">
<slot :item="item" :index="item.id" />
</li>
</ul>
</template>vue
<List :items="users">
<template #default="{ item, index }">
<strong>{{ index }}.</strong>
<span>{{ item.name }}</span>
</template>
</List>四、解构默认值
vue
<!-- 子组件 -->
<slot :user="user" :status="status">
{{ user.name }} - {{ status }} <!-- 父组件没传插槽时用这行 -->
</slot>五、useSlots 编程访问
vue
<script setup lang="ts">
import { useSlots } from 'vue'
const slots = useSlots()
console.log(slots.header ? '有 header' : '无 header')
</script>vue
<template>
<div v-if="slots.header" class="header">
<slot name="header" />
</div>
</template>六、动态插槽名
vue
<template>
<Layout>
<template #[dynamicName]>动态</template>
</Layout>
</template>
<script setup lang="ts">
import { ref } from 'vue'
const dynamicName = ref('footer')
</script>七、实战:可复用表格
vue
<!-- DataTable.vue -->
<script setup lang="ts" generic="T">
defineProps<{
rows: T[]
columns: { key: keyof T; title: string }[]
}>()
</script>
<template>
<table>
<thead>
<tr>
<th v-for="col in columns" :key="String(col.key)">{{ col.title }}</th>
</tr>
</thead>
<tbody>
<tr v-for="(row, i) in rows" :key="i">
<td v-for="col in columns" :key="String(col.key)">
<slot :name="String(col.key)" :row="row" :index="i">
{{ row[col.key] }}
</slot>
</td>
</tr>
</tbody>
</table>
</template>⚠️ 坑 2:通用组件
generic="T"是 Vue 3.3+ 特性,版本低了会编译失败。 ⚠️ 坑 3:插槽里的响应式数据,父组件访问时不会响应,要用v-for重新计算。
八、本章小结
| 类型 | 用途 |
|---|---|
<slot> | 默认插槽 |
<slot name="x"> | 具名插槽 |
<slot :data> | 作用域插槽 |
useSlots() | 在 setup 中访问 slots |
generic="T" | 泛型组件 |
动手练习
- 对话框:写
Modal.vue,用具名插槽header / body / footer - 作用域列表:写
UserList.vue,父组件自定义每行渲染方式 - 空状态:用
useSlots()判断是否有default插槽,没有就显示"暂无数据"
下一章:第 9 章:路由 →