第3章:组件化开发基础
第9节:插槽(Slots)系统
3.9.1 默认插槽与后备内容
插槽允许父组件向子组件的特定位置插入内容,使组件更加灵活和可复用。
默认插槽
默认插槽是最基本的插槽类型,子组件中使用<slot>标签定义插槽位置,父组件直接在子组件标签内插入内容:
<!-- 子组件 BaseLayout.vue -->
<template>
<div class="container">
<header>
<h1>网站标题</h1>
</header>
<main>
<!-- 默认插槽位置 -->
<slot></slot>
</main>
<footer>
<p>版权信息</p>
</footer>
</div>
</template>父组件使用:
<!-- 父组件 App.vue -->
<template>
<BaseLayout>
<!-- 插入到默认插槽的内容 -->
<h2>欢迎来到我的网站</h2>
<p>这是网站的主要内容</p>
</BaseLayout>
</template>后备内容
后备内容是当父组件没有提供插槽内容时显示的默认内容:
<!-- 子组件 Button.vue -->
<template>
<button class="btn">
<!-- 带后备内容的插槽 -->
<slot>默认按钮</slot>
</button>
</template>父组件使用:
<!-- 父组件 App.vue -->
<template>
<!-- 使用默认内容 -->
<Button></Button>
<!-- 提供自定义内容 -->
<Button>
自定义按钮
</Button>
</template>3.9.2 具名插槽
当子组件需要多个插槽时,可以使用具名插槽。子组件中使用name属性为插槽命名,父组件使用v-slot指令(或简写#)指定插槽名称:
<!-- 子组件 BaseLayout.vue -->
<template>
<div class="container">
<header>
<!-- 具名插槽:header -->
<slot name="header"></slot>
</header>
<main>
<!-- 默认插槽 -->
<slot></slot>
</main>
<footer>
<!-- 具名插槽:footer -->
<slot name="footer"></slot>
</footer>
</div>
</template>父组件使用:
<!-- 父组件 App.vue -->
<template>
<BaseLayout>
<!-- 插入到header插槽 -->
<template v-slot:header>
<h1>网站标题</h1>
<nav>
<a href="/">首页</a>
<a href="/about">关于</a>
</nav>
</template>
<!-- 插入到默认插槽 -->
<h2>欢迎来到我的网站</h2>
<p>这是网站的主要内容</p>
<!-- 插入到footer插槽 -->
<template v-slot:footer>
<p>© 2024 我的网站</p>
</template>
</BaseLayout>
</template>具名插槽的简写
v-slot: 可以简写为 #:
<template>
<BaseLayout>
<!-- 简写形式 -->
<template #header>
<h1>网站标题</h1>
</template>
<h2>欢迎来到我的网站</h2>
<p>这是网站的主要内容</p>
<template #footer>
<p>© 2024 我的网站</p>
</template>
</BaseLayout>
</template>3.9.3 作用域插槽
作用域插槽允许子组件向父组件传递数据,父组件可以使用这些数据来自定义插槽内容。
基本用法
子组件通过v-bind将数据传递给插槽,父组件使用v-slot指令接收数据:
<!-- 子组件 List.vue -->
<template>
<ul>
<li v-for="(item, index) in items" :key="item.id">
<!-- 将item和index传递给插槽 -->
<slot :item="item" :index="index"></slot>
</li>
</ul>
</template>
<script setup>
const props = defineProps({
items: {
type: Array,
default: () => []
}
})
</script>父组件使用:
<!-- 父组件 App.vue -->
<template>
<List :items="items">
<!-- 接收插槽传递的数据 -->
<template v-slot="slotProps">
{{ slotProps.index + 1 }}. {{ slotProps.item.name }}
</template>
</List>
</template>
<script setup>
const items = [
{ id: 1, name: '苹果' },
{ id: 2, name: '香蕉' },
{ id: 3, name: '橙子' }
]
</script>解构插槽参数
可以使用ES6解构语法简化插槽参数的使用:
<template>
<List :items="items">
<!-- 解构插槽参数 -->
<template v-slot="{ item, index }">
{{ index + 1 }}. {{ item.name }}
</template>
</List>
</template>具名作用域插槽
具名插槽也可以传递数据:
<!-- 子组件 Table.vue -->
<template>
<table>
<thead>
<tr>
<th v-for="column in columns" :key="column.key">
<slot :name="`header-${column.key}`" :column="column">
{{ column.title }}
</slot>
</th>
</tr>
</thead>
<tbody>
<tr v-for="row in rows" :key="row.id">
<td v-for="column in columns" :key="column.key">
<slot :name="`cell-${column.key}`" :row="row" :value="row[column.key]">
{{ row[column.key] }}
</slot>
</td>
</tr>
</tbody>
</table>
</template>父组件使用:
<template>
<Table :columns="columns" :rows="rows">
<!-- 自定义特定列的表头 -->
<template #header-name="{ column }">
<strong>{{ column.title }}</strong>
</template>
<!-- 自定义特定列的单元格 -->
<template #cell-actions="{ row }">
<button @click="edit(row)">编辑</button>
<button @click="delete(row)">删除</button>
</template>
</Table>
</template>3.9.4 动态插槽名与v-slot缩写
动态插槽名
Vue 2.6+支持动态插槽名,可以使用方括号语法动态指定插槽名称:
<template>
<BaseLayout>
<!-- 动态插槽名 -->
<template v-slot:[dynamicSlotName]>
<h1>动态插槽内容</h1>
</template>
</BaseLayout>
</template>
<script setup>
import { ref } from 'vue'
const dynamicSlotName = ref('header')
</script>v-slot缩写
v-slot: 可以简写为 #,但需要注意以下几点:
- 只有具名插槽才能使用简写
- 简写形式必须有参数
- 对于默认插槽,需要显式使用
#default
<template>
<BaseLayout>
<!-- 具名插槽简写 -->
<template #header>
<h1>网站标题</h1>
</template>
<!-- 默认插槽简写 -->
<template #default>
<p>网站内容</p>
</template>
<!-- 带参数的作用域插槽简写 -->
<template #footer="{ footerData }">
<p>{{ footerData.copyright }}</p>
</template>
<!-- 解构参数的作用域插槽简写 -->
<template #footer="{ copyright }">
<p>{{ copyright }}</p>
</template>
</BaseLayout>
</template>插槽使用最佳实践
- 合理使用插槽类型:根据需求选择合适的插槽类型(默认插槽、具名插槽、作用域插槽)
- 提供后备内容:为插槽提供合理的后备内容,提高组件的易用性
- 保持插槽简单:插槽内容应该简洁,复杂逻辑建议在父组件中处理
- 使用语义化的插槽名称:插槽名称应该清晰反映其用途
- 适当使用作用域插槽:只有当需要子组件传递数据时才使用作用域插槽
总结
插槽是Vue组件化开发中的重要特性,它允许父组件向子组件插入自定义内容,使组件更加灵活和可复用。通过掌握默认插槽、具名插槽和作用域插槽的使用,你可以编写出更加通用和强大的Vue组件。
在下一章中,我们将学习Vue.js的响应式系统与生命周期。