插槽 (Slot)是 Vue 中最强大的功能之一,它的核心作用是实现组件内容的分发,让子组件成为一个可定制、可复用的「容器」,父组件可以向子组件传递任意 HTML 结构、组件甚至逻辑,彻底解决了组件内容固化的问题。
一、插槽核心概念
简单理解:
- 子组件:定义插槽位置(相当于预留一个「空位」)。
- 父组件:向「空位」填充自定义内容(文本、HTML、组件都可以)。
- 如果父组件不填充内容,子组件可以设置默认内容。
二、Vue3 插槽完整用法
Vue3 完全兼容 Vue2 插槽语法,废弃了旧版语法 ,统一使用更简洁的 v-slot 指令(简写 #),语法更直观、语义更清晰。
1. 默认插槽(匿名插槽)
适用场景:子组件只有一个需要分发的内容,无需指定名称。
子组件(Child.vue)
使用 <slot> 标签定义插槽,可设置默认内容:
javascript
<template>
<div class="child-box">
<h3>子组件标题</h3>
<!-- 默认插槽:父组件不传递内容时,显示默认值 -->
<slot>我是插槽默认内容</slot>
</div>
</template>
父组件使用
直接在子组件标签内写内容,会自动填充到默认插槽:
javascript
<template>
<Child>
<!-- 填充到子组件默认插槽的内容 -->
<p>我是父组件传递的自定义内容</p>
<button>点击按钮</button>
</Child>
</template>
<script setup>
// Vue3 组合式 API
import Child from './Child.vue'
</script>
2. 具名插槽
适用场景:子组件有多个需要分发的位置,通过名称区分插槽。
语法:
- 子组件:
<slot name="插槽名"></slot> - 父组件:
#插槽名或v-slot:插槽名
子组件(Layout.vue)
javascript
<template>
<div class="layout">
<!-- 头部插槽 -->
<header>
<slot name="header"></slot>
</header>
<!-- 中间默认插槽 -->
<main>
<slot></slot>
</main>
<!-- 底部插槽 -->
<footer>
<slot name="footer"></slot>
</footer>
</div>
</template>
父组件使用
必须用 <template> 标签配合插槽名称指定内容位置:
javascript
<template>
<Layout>
<!-- 填充 header 插槽:#header 是 v-slot:header 的简写 -->
<template #header>
<h1>页面标题</h1>
</template>
<!-- 填充默认插槽(可写 #default 或省略) -->
<template #default>
<p>页面主体内容</p>
</template>
<!-- 填充 footer 插槽 -->
<template #footer>
<span>版权信息 © 2025</span>
</template>
</Layout>
</template>
<script setup>
import Layout from './Layout.vue'
</script>
3. 作用域插槽
核心价值:子组件向父组件传递数据,父组件可以使用子组件的数据渲染自定义内容。
适用场景:列表组件、表格组件(子组件提供数据,父组件决定展示样式)。
子组件(List.vue)
通过 v-bind 把数据绑定到插槽上,暴露给父组件:
javascript
<template>
<div class="list">
<slot
v-for="item in list"
:key="item.id"
<!-- 子组件暴露数据:自定义名称="子组件数据" -->
:item="item"
>
</slot>
</div>
</template>
<script setup>
import { ref } from 'vue'
// 子组件内部数据
const list = ref([
{ id: 1, name: 'Vue3 教程' },
{ id: 2, name: '插槽实战' },
{ id: 3, name: '组合式 API' }
])
</script>
父组件使用
接收子组件传递的数据,语法:#插槽名="接收对象"
javascript
<template>
<List>
<!-- 默认作用域插槽:scope 接收子组件传递的所有数据 -->
<template #default="scope">
<!-- 使用子组件的 item 数据 -->
<div class="item">
{{ scope.item.id }} - {{ scope.item.name }}
</div>
</template>
</List>
</template>
解构写法(更简洁,推荐):
javascript
<!-- 直接解构子组件传递的 item -->
<template #default="{ item }">
<div class="item">{{ item.id }} - {{ item.name }}</div>
</template>
4. 动态插槽名
Vue3 支持动态绑定插槽名称,灵活性拉满:
javascript
<template>
<Layout>
<!-- 动态插槽:slotName 是变量 -->
<template #[slotName]>
动态内容
</template>
</Layout>
</template>
<script setup>
import { ref } from 'vue'
const slotName = ref('header') // 可动态修改
</script>
三、Vue2 插槽用法(对比参考)
Vue2 分为旧语法和新语法(v-slot),Vue3 废弃了旧语法,只保留 v-slot 统一语法。
1. Vue2 默认插槽
javascript
<!-- 子组件 -->
<slot>默认内容</slot>
<!-- 父组件 -->
<Child>
父组件内容
</Child>
2. Vue2 具名插槽(旧语法 vs 新语法)
旧语法(废弃):slot="名称"
新语法(兼容):v-slot:名称(Vue 2.6+ 支持)
javascript
<!-- 旧语法(不推荐,Vue3 已删除) -->
<template slot="header">
头部内容
</template>
<!-- 新语法(和 Vue3 一致) -->
<template v-slot:header>
头部内容
</template>
3. Vue2 作用域插槽(旧语法 vs 新语法)
旧语法(废弃):slot-scope
新语法(兼容):v-slot
javascript
<!-- 旧语法 -->
<template slot="default" slot-scope="scope">
{{ scope.item }}
</template>
<!-- 新语法(和 Vue3 一致) -->
<template v-slot:default="scope">
{{ scope.item }}
</template>
四、Vue2 与 Vue3 插槽核心区别
| 特性 | Vue2 | Vue3 |
|---|---|---|
| 推荐语法 | 2.6+ 支持 v-slot,兼容旧语法 |
仅支持 v-slot(简写 #),废弃所有旧语法 |
| 旧语法 | 支持 slot、slot-scope |
完全废弃,不再支持 |
| 作用域插槽 | 必须写在 <template> 上(新语法) |
规则一致,更简洁 |
| 动态插槽 | 支持 | 支持,语法更简洁 |
| 写法限制 | 无特殊限制 | 具名插槽必须写在 <template> 上 |
一句话总结:Vue3 插槽是 Vue2 新语法的精简版,去掉了冗余语法,统一使用 #插槽名,学习成本更低,代码更简洁。
五、插槽使用注意事项
- 具名插槽必须配合
<template>:除了默认插槽,具名插槽 / 作用域插槽都要写在<template>标签内。 - 默认插槽简写 :
#default可以省略,直接写内容。 - 数据作用域:父组件插槽内容只能访问父组件数据,子组件插槽内容只能访问子组件数据(作用域插槽除外)。
- 默认内容优先级:父组件传递内容 → 覆盖子组件默认内容;不传递 → 显示默认内容。
六、实战场景:封装通用卡片组件
用 Vue3 插槽封装一个高复用卡片组件,演示默认 + 具名 + 作用域插槽组合使用:
子组件(Card.vue)
javascript
<template>
<div class="card">
<!-- 卡片头部 -->
<div class="card-header">
<slot name="header">默认标题</slot>
</div>
<!-- 卡片内容 -->
<div class="card-body">
<slot>默认内容</slot>
</div>
<!-- 卡片底部:作用域插槽 -->
<div class="card-footer">
<slot name="footer" :time="currentTime"></slot>
</div>
</div>
</template>
<script setup>
import { ref } from 'vue'
const currentTime = ref(new Date().toLocaleString())
</script>
父组件使用
javascript
<template>
<Card>
<!-- 头部 -->
<template #header>
<h4>自定义卡片标题</h4>
</template>
<!-- 内容 -->
<p>这是卡片的自定义主体内容</p>
<!-- 底部:接收子组件时间 -->
<template #footer="{ time }">
<span>更新时间:{{ time }}</span>
</template>
</Card>
</template>
总结
- 插槽本质:组件内容分发工具,让子组件支持自定义内容。
- Vue3 核心语法 :默认插槽、
#名称(具名插槽)、#名称="数据"(作用域插槽)。 - 与 Vue2 差异 :废弃
slot/slot-scope旧语法,统一使用v-slot简写#,更简洁规范。 - 最佳实践:封装通用组件(列表、弹窗、布局、卡片)必用插槽,是 Vue 组件化开发的核心技能。
个人速记:
具名插槽 子组件用<scope name="xx">接收,父组件<template #xx>插入
作用域插槽 子组件用<scope name="xx" :变量名="item">,父组件<template #xxx="{ 变量名 }"> <span>更新时间:{``{ 变量名 }}</span> </template>接收子组件变量