下面,我们来系统的梳理关于 Vue 插槽 的基本知识点
一、插槽核心思想
插槽(Slot) 是 Vue 组件化的核心机制,用于实现 内容分发。它允许父组件向子组件传递 模板片段,使子组件具备更强的灵活性和复用性。插槽的核心价值在于:
- 组件结构定制:父组件可自定义子组件的部分内容
- UI 与逻辑解耦:子组件专注数据处理,父组件控制显示样式
- 高阶组件模式:实现渲染委托、作用域隔离等高级特性
二、基础插槽类型
1. 默认插槽(匿名插槽)
作用:接收父组件传递的 全部未命名内容
<!-- 子组件 ChildComponent.vue -->
<template>
<div class="card">
<slot>默认内容(父组件未传内容时显示)</slot>
</div>
</template>
<!-- 父组件使用 -->
<ChildComponent>
<p>这里的内容会替换 slot 标签</p>
</ChildComponent>
特性:
- 一个组件只能有一个默认插槽
- 支持设置默认内容(当父组件不传内容时显示)
2. 具名插槽(Named Slots)
作用:实现 多区块内容分发
<!-- 子组件 Layout.vue -->
<template>
<div class="container">
<header>
<slot name="header"></slot>
</header>
<main>
<slot></slot> <!-- 默认插槽 -->
</main>
<footer>
<slot name="footer"></slot>
</footer>
</div>
</template>
<!-- 父组件使用 -->
<Layout>
<template v-slot:header>
<h1>页面标题</h1>
</template>
<p>主内容区域(默认插槽)</p>
<template #footer> <!-- 简写语法 -->
<p>版权信息 © 2023</p>
</template>
</Layout>
关键点:
- 使用
name
属性定义插槽名称(未命名即为默认插槽) - Vue2 使用
slot="name"
,Vue3 推荐v-slot:name
或#name
- 不同插槽可 独立控制内容
3. 作用域插槽(Scoped Slots)
作用:子组件向父组件 传递数据,实现 渲染委托
<!-- 子组件 DataList.vue -->
<template>
<ul>
<li v-for="item in items" :key="item.id">
<slot :item="item" :index="index"></slot>
</li>
</ul>
</template>
<!-- 父组件使用 -->
<DataList :items="userList">
<template #default="{ item, index }"> <!-- Vue3 解构语法 -->
<span>{{ index + 1 }}. {{ item.name }}(年龄:{{ item.age }})</span>
</template>
</DataList>
核心机制:
- 子组件通过
<slot :data="data">
暴露数据 - 父组件通过
v-slot:name="slotProps"
接收数据 - 支持 解构赋值 和 重命名:
<template #header="{ title: newTitle }"> {{ newTitle }} </template>
三、插槽特性详解
1. 编译作用域规则
- 父级模板:所有内容在父级作用域中编译
- 子级模板:所有内容在子级作用域中编译
<!-- 父组件 -->
<Child>
<!-- 此处访问父组件数据 -->
{{ parentData }}
<div :style="parentStyle"></div>
</Child>
<!-- 子组件 -->
<slot>
<!-- 此处访问子组件数据 -->
{{ childData }}
</slot>
2. 动态插槽名
<template v-slot:[dynamicSlotName]>
动态内容
</template>
<script>
export default {
data() {
return {
dynamicSlotName: 'header'
}
}
}
</script>
3. 插槽复用与组合
<!-- 将多个插槽内容合并 -->
<template #default="slotProps">
<slot name="prefix"></slot>
{{ slotProps.data }}
<slot name="suffix"></slot>
</template>
四、Vue2 与 Vue3 语法对比
特性 | Vue2 语法 | Vue3 语法 |
---|---|---|
具名插槽 | <template slot="name"> | <template v-slot:name> 或 #name |
作用域插槽 | slot-scope="props" | v-slot:name="props" |
默认插槽作用域 | 无法直接使用 | 可用 #default="props" |
插槽属性 | 不支持 | 支持 <slot :item="data"> |
五、最佳实践与性能优化
1. 设计原则
- 单一职责:每个插槽应有明确的内容定位
- 适度抽象:避免超过 3 层插槽嵌套
- 命名规范:使用语义化名称(如
#header
、#item
)
2. 性能优化技巧
- 对静态内容使用
v-once
:<template #header> <div v-once>固定标题</div> </template>
- 避免在插槽内进行复杂计算(移至计算属性)
- 作用域插槽传递 最小必要数据,避免传递大型对象
3. 常见错误
<!-- 错误:混用默认与具名插槽 -->
<Child>
<div>默认内容</div>
<template #footer>页脚</template>
<div>其他内容</div> <!-- 此处内容会被丢弃 -->
</Child>
<!-- 正确:分离具名插槽 -->
<Child>
<template #default>
<div>默认内容</div>
<div>其他内容</div>
</template>
<template #footer>页脚</template>
</Child>
六、实战案例
1. 可定制表格组件
<!-- TableComponent.vue -->
<template>
<table>
<thead>
<tr>
<th v-for="col in columns" :key="col.key">
<slot :name="`header-${col.key}`" :column="col">
{{ col.title }} <!-- 默认显示列标题 -->
</slot>
</th>
</tr>
</thead>
<tbody>
<tr v-for="(row, index) in data" :key="row.id">
<td v-for="col in columns" :key="col.key">
<slot :name="`cell-${col.key}`" :row="row" :index="index">
{{ row[col.key] }} <!-- 默认显示单元格数据 -->
</slot>
</td>
</tr>
</tbody>
</table>
</template>
使用示例:
<TableComponent :columns="columns" :data="userData">
<!-- 自定义邮箱列标题 -->
<template #header-email>
电子邮箱 <span class="required">*</span>
</template>
<!-- 自定义操作列单元格 -->
<template #cell-actions="{ row }">
<button @click="editUser(row)">编辑</button>
</template>
</TableComponent>
2. 复合布局系统
<!-- LayoutSystem.vue -->
<template>
<div class="layout">
<div class="sidebar">
<slot name="sidebar"></slot>
</div>
<div class="main">
<div class="header">
<slot name="header"></slot>
</div>
<div class="content">
<slot></slot>
</div>
</div>
</div>
</template>
<!-- 使用案例 -->
<LayoutSystem>
<template #sidebar>
<NavigationMenu />
</template>
<template #header>
<SearchBar />
</template>
<ArticleContent />
</LayoutSystem>
七、总结表格
插槽类型 | 语法 | 核心能力 | 典型场景 |
---|---|---|---|
默认插槽 | <slot> | 基础内容分发 | 通用容器组件 |
具名插槽 | <slot name="header"> | 多区块内容控制 | 布局系统、复杂卡片 |
作用域插槽 | <slot :data="row"> | 子向父传递数据 | 表格组件、列表渲染 |
动态插槽 | v-slot:[dynamicName] | 运行时决定插槽位置 | 可配置化组件 |