插槽
什么是插槽?
插槽(Slot)是 Vue 组件中的一个重要概念,它允许父组件向子组件传递 HTML 内容,实现组件的内容分发。
1. 默认插槽
默认插槽是最基本的插槽类型,允许父组件向子组件传递任意内容。
子组件(定义插槽)
<template>
<div class="card">
<div class="card-header">
<h3>卡片标题</h3>
</div>
<div class="card-body">
<!-- 默认插槽 -->
<slot>默认内容</slot>
</div>
<div class="card-footer">
卡片底部
</div>
</div>
</template>
父组件(使用插槽)
<template>
<div>
<CardComponent>
<p>这是卡片的自定义内容</p>
<button @click="handleClick">点击按钮</button>
</CardComponent>
<!-- 不提供内容,使用默认值 -->
<CardComponent></CardComponent>
</div>
</template>
2. 具名插槽
具名插槽允许父组件向子组件的不同位置传递内容,通过名称来区分。
子组件(定义多个具名插槽)
<template>
<div class="layout">
<header class="header">
<!-- 具名插槽:header -->
<slot name="header">默认头部</slot>
</header>
<main class="main">
<!-- 默认插槽,等同于 name="default" -->
<slot>默认内容</slot>
</main>
<aside class="sidebar">
<!-- 具名插槽:sidebar -->
<slot name="sidebar">默认侧边栏</slot>
</aside>
<footer class="footer">
<!-- 具名插槽:footer -->
<slot name="footer">默认底部</slot>
</footer>
</div>
</template>
父组件(使用具名插槽)
<template>
<LayoutComponent>
<!-- 使用 v-slot 指令指定插槽名称 -->
<template v-slot:header>
<h1>页面标题</h1>
</template>
<!-- 默认插槽内容 -->
<p>这是页面的主要内容</p>
<p>可以包含多个元素</p>
<!-- 简写语法:#sidebar -->
<template #sidebar>
<ul>
<li>菜单项 1</li>
<li>菜单项 2</li>
<li>菜单项 3</li>
</ul>
</template>
<template #footer>
<p>版权所有 © 2025</p>
</template>
</LayoutComponent>
</template>
3. 作用域插槽
作用域插槽允许子组件向父组件传递数据,父组件可以在插槽内容中使用这些数据。
子组件(向插槽传递数据)
<template>
<div class="list">
<h3>用户列表</h3>
<ul>
<li v-for="user in users" :key="user.id">
<!-- 作用域插槽,传递 user 数据 -->
<slot :user="user" :index="$index">
{{ user.name }} - {{ user.email }}
</slot>
</li>
</ul>
</div>
</template>
<script>
export default {
data() {
return {
users: [
{ id: 1, name: 'John', email: 'john@example.com' },
{ id: 2, name: 'Jane', email: 'jane@example.com' },
{ id: 3, name: 'Bob', email: 'bob@example.com' }
]
}
}
}
</script>
父组件(使用插槽数据)
<template>
<UserListComponent>
<!-- 使用 v-slot 接收插槽数据 -->
<template v-slot:default="slotProps">
<div class="user-item">
<strong>{{ slotProps.index + 1 }}. {{ slotProps.user.name }}</strong>
<span class="email">{{ slotProps.user.email }}</span>
</div>
</template>
</UserListComponent>
<!-- 解构插槽数据 -->
<UserListComponent>
<template #default="{ user, index }">
<div class="user-item compact">
{{ index + 1 }}. {{ user.name }}
</div>
</template>
</UserListComponent>
</template>
4. 动态插槽名
Vue 3 允许使用动态插槽名,增加了插槽使用的灵活性。
父组件(使用动态插槽名)
<template>
<ComponentWithSlots>
<!-- 动态插槽名 -->
<template v-slot:[dynamicSlotName]>
<p>动态插槽内容</p>
</template>
<!-- 简写形式 -->
<template #[dynamicSlotName]>
<p>动态插槽内容</p>
</template>
</ComponentWithSlots>
</template>
<script>
export default {
data() {
return {
dynamicSlotName: 'header'
}
}
}
</script>
5. 插槽的默认值
可以为插槽设置默认内容,当父组件没有提供插槽内容时,将显示默认内容。
子组件(设置插槽默认值)
<template>
<div class="card">
<div class="card-body">
<slot>
<p>这是默认内容</p>
<button>默认按钮</button>
</slot>
</div>
</div>
</template>
插槽的使用场景
- 组件库开发,允许用户自定义组件内容
- 布局组件,如 header、main、sidebar、footer 结构
- 列表组件,允许用户自定义列表项的渲染方式
- 卡片组件,允许用户自定义卡片内容
- 模态框、对话框等组件,允许用户自定义内容
最佳实践
- 为插槽提供有意义的默认内容
- 使用具名插槽时,名称要清晰易懂
- 作用域插槽传递的数据要精简,只传递必要的数据
- 合理使用插槽,避免过度复杂的插槽结构
- 在组合式 API 中,可以使用
useSlots和useAttrs访问插槽和属性