Vue插槽(Slot)是组件间内容分发的核心机制,用于解决"父组件向子组件传递模板片段"的需求,实现组件的灵活复用与结构解耦。简单来说,插槽就是子组件中预留的"内容占位符",占位符的具体内容由父组件决定,子组件仅负责固定布局和逻辑,让组件既能保持统一风格,又能灵活适配不同场景。
本文将详细讲解Vue插槽的核心概念、3种核心用法(默认插槽、具名插槽、作用域插槽),明确Vue2与Vue3的语法差异,提供可直接复制的实战示例,同时梳理常见问题,兼顾新手入门与实战开发需求。
一、插槽核心基础(必懂)
插槽的核心逻辑可类比为"函数传参":父组件向子组件传递"模板内容"(相当于函数参数),子组件通过<slot>标签(相当于函数接收参数的位置)接收并渲染内容,最终实现"子组件定结构、父组件定内容"的复用效果。
核心要点:
- 插槽内容可是任意合法模板(文本、标签、组件等),不局限于简单文本;
- 插槽内容的作用域:插槽内容定义在父组件,因此只能访问父组件的数据,无法直接访问子组件的数据(需用作用域插槽解决);
- Vue2与Vue3插槽核心功能一致,仅在具名插槽、作用域插槽的语法上有差异,下文将分别标注适配版本。
二、Vue插槽3种核心用法(实战重点)
按"基础到复杂"排序,默认插槽适用于简单内容分发,具名插槽适用于多区域内容分发,作用域插槽适用于子组件向父组件传递数据后,父组件自定义渲染内容。
1. 默认插槽(匿名插槽)------ 最简单的内容分发
默认插槽是最基础的插槽形式,子组件中仅定义一个无名称的<slot>标签,父组件传入的所有未命名内容,都会自动填充到这个插槽中。Vue2与Vue3用法基本一致。
实战示例(Vue2+Vue3通用)
javascript
// 1. 子组件(SlotDefault.vue)------ 定义默认插槽
<template>
<div class="slot-container">
<!-- 插槽出口:未命名,即为默认插槽 -->
<slot>
<!-- 后备内容(默认内容):父组件未传入内容时显示 -->
这是默认插槽的后备内容(父组件未传内容时显示)
</slot>
</div>
</template>
<style scoped>
.slot-container {
padding: 20px;
border: 1px solid #eee;
border-radius: 8px;
}
</style>
// 2. 父组件 ------ 使用默认插槽
<template>
<div>
<h3>默认插槽用法</h3>
<!-- 方式1:传入简单文本 -->
<SlotDefault>父组件传入的简单文本内容</SlotDefault>
<!-- 方式2:传入复杂内容(标签+组件) -->
<SlotDefault>
<span style="color: #42b983;">父组件传入的带样式文本</span>
<button>父组件传入的按钮</button>
<!-- 传入其他组件 -->
<OtherComponent />
</SlotDefault>
<!-- 方式3:不传入内容(显示子组件的后备内容) -->
<SlotDefault />
</div>
</template>
<script setup>
// Vue3 需引入子组件
import SlotDefault from './SlotDefault.vue'
import OtherComponent from './OtherComponent.vue'
</script>
// Vue2 脚本写法(父组件)
<script>
import SlotDefault from './SlotDefault.vue'
import OtherComponent from './OtherComponent.vue'
export default {
components: { SlotDefault, OtherComponent }
}
</script>
说明:子组件<slot>标签内的内容为"后备内容",仅当父组件未传入任何插槽内容时才会显示,传入内容后会自动替换后备内容。
2. 具名插槽 ------ 多区域内容精准分发
当子组件需要多个不同的内容占位区域(如页面布局的头部、主体、底部)时,默认插槽无法满足需求,此时需使用具名插槽。通过给<slot>标签添加name属性命名,父组件可精准将内容分发到对应插槽,Vue2与Vue3语法差异较大。
实战示例(Vue2 vs Vue3)
javascript
// 1. 子组件(SlotNamed.vue)------ 定义具名插槽(Vue2+Vue3通用)
<template>
<div class="layout">
<!-- 头部插槽:name="header" -->
<slot name="header">默认头部</slot>
<!-- 主体插槽:name="main" -->
<slot name="main">默认主体</slot>
<!-- 底部插槽:name="footer" -->
<slot name="footer">默认底部</slot>
</div>
</template>
<style scoped>
.layout {
display: flex;
flex-direction: column;
gap: 10px;
}
.layout > div { padding: 10px; border: 1px solid #eee; }
</style>
// 2. 父组件使用 ------ Vue2 写法
<template>
<SlotNamed>
<!-- 用 slot 属性指定插槽名称,已废弃(Vue2.6+推荐用v-slot) -->
<div slot="header">Vue2 头部内容(自定义)</div>
<div slot="main">Vue2 主体内容(自定义)</div>
<div slot="footer">Vue2 底部内容(自定义)</div>
<!-- Vue2.6+ 推荐写法:template + v-slot -->
<template v-slot:header>
<div>Vue2.6+ 头部内容(自定义)</div>
</template>
<template v-slot:main>
<div>Vue2.6+ 主体内容(自定义)</div>
</template>
<template v-slot:footer>
<div>Vue2.6+ 底部内容(自定义)</div>
</template>
</SlotNamed>
</template>
// 3. 父组件使用 ------ Vue3 写法(核心:废弃slot属性,统一用v-slot)
<template>
<SlotNamed>
<!-- 语法:template + v-slot:插槽名,可简写为 #插槽名 -->
<template #header>
<div>Vue3 头部内容(自定义)</div>
</template>
<template #main>
<div>Vue3 主体内容(自定义)</div>
</template>
<template #footer>
<div>Vue3 底部内容(自定义)</div>
</template>
<!-- 未命名内容,自动分发到默认插槽(若子组件有默认插槽) -->
<div>默认插槽内容(未命名)</div>
</SlotNamed>
</template>
<script setup>
import SlotNamed from './SlotNamed.vue'
</script>
关键差异:Vue2支持slot属性和v-slot两种写法,Vue3仅支持v-slot(简写为#),且必须配合<template>标签使用(默认插槽可省略<template>)。
3. 作用域插槽 ------ 子传父数据+父自定义渲染
默认插槽和具名插槽,只能实现"父组件向子组件传递内容",无法让插槽内容访问子组件的数据。作用域插槽解决了这一问题:子组件通过v-bind将自身数据绑定到<slot>标签上(称为"插槽属性"),父组件接收这些数据后,可根据子组件数据自定义插槽内容的渲染方式。
核心场景:子组件有数据(如列表数据),但渲染样式由父组件决定(如列表项可渲染为文字、按钮、卡片)。
实战示例(Vue2 vs Vue3)
javascript
// 1. 子组件(SlotScoped.vue)------ 绑定子组件数据(Vue2+Vue3通用)
<template>
<div class="list">
<!-- 子组件数据:列表数组 -->
<div v-for="(item, index) in list" :key="index">
<!-- 绑定子组件数据到插槽::item="item" :index="index" -->
<slot :item="item" :index="index">
<!-- 后备内容:父组件未自定义渲染时显示 -->
{{ item.name }}(默认渲染)
</slot>
</div>
</div>
</template>
<script setup>
// Vue3 脚本
import { ref } from 'vue'
const list = ref([
{ id: 1, name: 'Vue基础', type: '前端' },
{ id: 2, name: '插槽用法', type: '前端' },
{ id: 3, name: '路由跳转', type: '前端' }
])
</script>
// Vue2 脚本(子组件)
<script>
export default {
data() {
return {
list: [
{ id: 1, name: 'Vue基础', type: '前端' },
{ id: 2, name: '插槽用法', type: '前端' },
{ id: 3, name: '路由跳转', type: '前端' }
]
}
}
}
</script>
// 2. 父组件使用 ------ Vue2 写法
<template>
<SlotScoped>
<!-- 方式1:slot-scope 接收插槽属性(Vue2.6-) -->
<div slot-scope="slotProps">
索引:{{ slotProps.index }} | 名称:{{ slotProps.item.name }}
</div>
<!-- 方式2:v-slot 接收(Vue2.6+ 推荐,可解构) -->
<template v-slot:default="slotProps">
<!-- 解构简化:直接提取item和index -->
<template v-slot:default="{ item, index }">
索引{{ index + 1 }}:{{ item.name }}({{ item.type }})
</template>
</template>
</SlotScoped>
</template>
// 3. 父组件使用 ------ Vue3 写法(核心:废弃slot-scope,统一用v-slot接收)
<template>
<SlotScoped>
<!-- 方式1:完整写法,接收所有插槽属性 -->
<template #default="slotProps">
索引:{{ slotProps.index }} | 名称:{{ slotProps.item.name }}
</template>
<!-- 方式2:解构简化(推荐),可设置默认值避免报错 -->
<template #default="{ item = { name: '默认名称' }, index = 0 }">
索引{{ index + 1 }}:{{ item.name }}({{ item.type }})
<button @click="handleClick(item.id)">查看详情</button>
</template>
</SlotScoped>
</template>
<script setup>
import SlotScoped from './SlotScoped.vue'
const handleClick = (id) => {
console.log('查看ID为', id, '的详情')
}
</script>
关键差异:Vue2用slot-scope或v-slot接收插槽属性,Vue3仅用v-slot接收,且支持ES6解构赋值,可设置默认值提升组件健壮性。
三、Vue2与Vue3插槽语法差异汇总
为方便快速区分和迁移,整理核心差异如下,重点关注Vue3的语法规范:
| 插槽类型 | Vue2 语法 | Vue3 语法 | 核心差异 |
|---|---|---|---|
| 默认插槽 | 直接在子组件标签内写内容;支持 |