一、插槽的核心概念与作用
定义:插槽是 Vue 实现组件内容分发的机制,允许父组件向子组件指定位置插入 HTML 结构,类似“占位符”。
核心作用:
- 实现组件的灵活复用(如布局组件、表单组件);
- 分离组件逻辑与内容,符合“关注点分离”原则;
- 结合作用域插槽实现父子组件数据双向流通。
二、Vue 2 与 Vue 3 插槽语法对比表
特性 | Vue 2 写法 | Vue 3 写法 | 核心变化 |
---|---|---|---|
默认插槽 | <template slot-scope="props"> | <template #default="props"> | 统一用 v-slot 或 # 前缀,简化语法 |
具名插槽 | <template slot="header"> | <template #header> | 移除 slot 属性,改用 v-slot: |
作用域参数 | slot-scope="props" | v-slot="props" | 参数接收方式统一 |
缩写语法 | #header | #header | 保留 # 缩写,更简洁 |
参数解构 | 不支持直接解构 | 支持 v-slot="{ user }" 直接解构 | 提升模板可读性 |
三、具体写法与示例对比
1. 默认插槽(匿名插槽)
<!-- 子组件(通用卡片) -->
<template>
<div class="card">
<slot>默认内容(无传入时显示)</slot>
</div>
</template>
<!-- Vue 2 父组件 -->
<CardComponent>
<template slot-scope="props">
<p>父组件传入内容:{{ props.data }}</p>
</template>
</CardComponent>
<!-- Vue 3 父组件 -->
<CardComponent>
<template #default="props">
<p>父组件传入内容:{{ props.data }}</p>
</template>
</CardComponent>
<!-- Vue 3 简化写法(省略 #default) -->
<CardComponent #="{ data }">
<p>父组件传入内容:{{ data }}</p>
</CardComponent>
2. 具名插槽(指定位置插入)
<!-- 子组件(布局组件) -->
<template>
<div class="layout">
<header>
<slot name="header">默认头部</slot>
</header>
<main>
<slot>默认主体</slot>
</main>
<footer>
<slot name="footer">默认底部</slot>
</footer>
</div>
</template>
<!-- Vue 2 父组件 -->
<LayoutComponent>
<template slot="header">
<h1>自定义头部</h1>
</template>
<p>自定义主体内容</p>
<template slot="footer">
<p>© 2023 版权所有</p>
</template>
</LayoutComponent>
<!-- Vue 3 父组件 -->
<LayoutComponent>
<template #header>
<h1>自定义头部</h1>
</template>
<p>自定义主体内容</p>
<template #footer>
<p>© 2023 版权所有</p>
</template>
</LayoutComponent>
<!-- Vue 3 更简洁写法 -->
<LayoutComponent>
<h1 #header>自定义头部</h1>
<p>自定义主体内容</p>
<p #footer>© 2023 版权所有</p>
</LayoutComponent>
3. 作用域插槽(子组件向父组件传数据)
<!-- 子组件(列表项) -->
<template>
<div class="list-item">
<slot :item="item" :index="index">
<span>{{ item.name }}</span>
</slot>
</div>
</template>
<script>
export default {
props: {
item: Object,
index: Number
}
}
</script>
<!-- Vue 2 父组件 -->
<ListItem :item="user" :index="0">
<template slot-scope="scope">
<div>
<span>序号:{{ scope.index + 1 }}</span>
<span>姓名:{{ scope.item.name }}</span>
</div>
</template>
</ListItem>
<!-- Vue 3 父组件 -->
<ListItem :item="user" :index="0">
<template #default="{ item, index }">
<div>
<span>序号:{{ index + 1 }}</span>
<span>姓名:{{ item.name }}</span>
</div>
</template>
</ListItem>
四、核心区别与原理分析
-
语法统一化
- Vue 2 中
slot
(具名)与slot-scope
(作用域)是独立语法,易混淆; - Vue 3 用
v-slot
(可缩写为#
)统一所有插槽场景,参数通过v-slot="props"
接收,避免语法碎片化。
- Vue 2 中
-
参数解构支持
- Vue 3 允许直接解构插槽参数(如
#default="{ item, index }"
),减少模板中的scope.item
嵌套访问,提升可读性; - Vue 2 需通过
slot-scope="scope"
接收后再访问scope.item
。
- Vue 3 允许直接解构插槽参数(如
-
编译规则变化
- Vue 3 要求插槽内容必须包裹在
<template>
中(除非是单个元素),否则报错; - Vue 2 中可直接在组件标签内写入内容(如
<Card>内容</Card>
),但不利于语法统一。
- Vue 3 要求插槽内容必须包裹在
-
性能优化
- Vue 3 的插槽编译结果更高效,减少了运行时的函数生成开销;
- 作用域插槽的参数传递方式优化,避免不必要的组件更新。
五、问题
1. 问:Vue 3 为什么要改变插槽语法?
- 答:
Vue 2 中slot
和slot-scope
共存导致语法混乱(如具名插槽用slot
,作用域插槽用slot-scope
),开发者需记忆多套规则。Vue 3 用v-slot
统一语法,通过#
缩写保持简洁,同时支持参数解构,降低学习成本并提升代码可读性。
2. 问:Vue 3 中如何省略默认插槽的写法?
- 答:
当插槽为默认插槽时,可直接在组件标签上使用<!-- 完整写法 --> <Component> <template #default="{ data }"> <p>{{ data }}</p> </template> </Component> <!-- 简化写法(省略 <template> 和 #default) --> <Component #="{ data }"> <p>{{ data }}</p> </Component>
#
前缀,且无需包裹<template>
(只要内容是单个元素)。
3. 问:作用域插槽在 Vue 2 和 Vue 3 中的本质区别?
- 答:
- 本质相同:均通过函数参数传递子组件数据,父组件接收后渲染;
- 语法差异:
Vue 2 通过slot-scope
定义参数(如slot-scope="props"
),
Vue 3 通过v-slot
接收参数(如#default="props"
),并支持解构(如#default="{ user }"
)。