34.Vue 插槽(Slot)终极学习比较指南默认插槽、默认插槽、具名插槽、作用域插槽的详细建设当子组件有多个位置需要透传数据时,父组件就写多个 <template #名字="数据对象">,各管各的,互不打架。
Vue 插槽(Slot)终极学习比较指南
这是一份从入门到精通的 Vue 插槽全景对比,帮你彻底理清所有概念。
一、核心概念总览表
|
插槽类型 |
别名 |
核心作用 |
数据流向 |
难度 |
|---|---|---|---|---|
|
默认插槽 |
匿名插槽 |
父传子(结构) |
父 → 子 |
⭐ |
|
具名插槽 |
多插槽 |
精准投放内容 |
父 → 子 |
⭐⭐ |
|
作用域插槽 |
带数据的插槽 |
子传父(数据) |
子 → 父 |
⭐⭐⭐⭐ |
|
后备内容 |
默认值 |
保底显示 |
无 |
⭐ |
二、详细对比分析
1️⃣ 默认插槽(最基础)
子组件 (Child.vue)
<template>
<div class="card">
<!-- 挖个坑 -->
<slot></slot>
</div>
</template>
父组件 (Parent.vue)
<template>
<Child>
<!-- 填坑(不需要 v-slot) -->
<p>我是父组件的内容</p>
</Child>
</template>
✅ 特点:
-
最简单
-
只能有一个默认坑
-
父组件内容直接用
2️⃣ 具名插槽(多坑专用)
子组件 (Layout.vue)
<template>
<div class="layout">
<header>
<slot name="header"></slot>
</header>
<main>
<slot></slot> <!-- 默认插槽 -->
</main>
<footer>
<slot name="footer"></slot>
</footer>
</div>
</template>
父组件
<template>
<Layout>
<!-- 简写:#header -->
<template #header>
<h1>网站标题</h1>
</template>
<!-- 默认插槽内容 -->
<p>主要内容区域</p>
<template #footer>
<p>© 2024 版权所有</p>
</template>
</Layout>
</template>
✅ 特点:
-
一个组件多个坑
-
精准投放
-
常用于布局组件
3️⃣ 作用域插槽(最难但最强 🔥)
子组件 (Table.vue) —— 拥有数据
<template>
<ul>
<li v-for="item in list" :key="item.id">
<!-- 把数据传给父组件 -->
<slot :row="item" :index="index"></slot>
</li>
</ul>
</template>
<script>
export default {
data() {
return {
list: [
{ id: 1, name: '张三', age: 20 },
{ id: 2, name: '李四', age: 25 }
]
}
}
}
</script>
父组件 —— 决定怎么显示
<template>
<Table>
<!-- 接收子组件的数据 -->
<template #default="slotProps">
<span>{{ slotProps.row.name }}</span> -
<span>{{ slotProps.row.age }}岁</span>
</template>
</Table>
</template>
✅ 特点:
-
子组件给数据
-
父组件定结构
-
UI 组件库的核心技术
三、语法演变史(Vue 2 → Vue 3)
|
版本 |
写法 |
状态 |
|---|---|---|
|
Vue 2 |
|
❌ 废弃 |
|
Vue 2.6+ |
|
✅ 推荐 |
|
Vue 3 |
|
✅ 最简 |
简写对照
<!-- 完整写法 -->
<template v-slot:header="data"></template>
<!-- 简写 -->
<template #header="data"></template>
<!-- 无参数简写 -->
<template v-slot="data"></template>
<!-- 等同于 -->
<template #default="data"></template>
四、数据流向对比图
默认插槽 / 具名插槽:
父组件 (HTML结构) ──────→ 子组件 (坑位)
作用域插槽:
子组件 (数据 row) ──────→ 父组件 (使用数据)
五、真实项目应用场景
🎯 场景 1:通用按钮组件
<!-- Button.vue -->
<button class="btn">
<slot>默认按钮</slot> <!-- 后备内容 -->
</button>
<!-- 使用 -->
<Button>提交</Button> <!-- 显示:提交 -->
<Button></Button> <!-- 显示:默认按钮 -->
🎯 场景 2:表格组件(核心应用)
<!-- Table.vue -->
<table>
<tr v-for="item in data">
<td>{{ item.id }}</td>
<td>
<!-- 把整行数据给父组件 -->
<slot name="actions" :row="item"></slot>
</td>
</tr>
</table>
<!-- 使用 -->
<Table :data="users">
<template #actions="{ row }">
<button @click="edit(row)">编辑</button>
<button @click="delete(row)">删除</button>
</template>
</Table>
六、常见错误与避坑指南 ⚠️
❌ 错误 1:在父组件访问子组件数据
<!-- ❌ 错误 -->
<Child>
{{ childData }} <!-- 访问不到! -->
</Child>
✅ 正确做法:用作用域插槽
<Child v-slot="slotProps">
{{ slotProps.childData }}
</Child>
❌ 错误 2:具名插槽语法混乱
<!-- ❌ 错误 -->
<div slot="header"></div>
<!-- ✅ 正确 -->
<template v-slot:header></template>
<!-- 或 -->
<template #header></template>
七、一句话总结
|
你想做什么? |
用什么插槽? |
|---|---|
|
传一段 HTML |
默认插槽 |
|
传多段 HTML |
具名插槽 |
|
子组件给父组件数据 |
作用域插槽 |
|
组件有默认内容 |
后备内容 |
💡 终极心法:
插槽是“结构”的传递,作用是“数据”的回流。
两者结合 = 组件设计的终极形态。
如果你愿意,我可以下一步帮你用你现在的后台项目,写一个真实可用的 Table 组件,直接用上所有插槽知识 👍
这是一个非常容易卡住初学者的点。我们把 <template #default="slotProps">拆碎了来讲,你看完一定能懂。
一句话先给结论 ✅
这行代码的意思是:
👉 父组件在说:
“我要接收子组件通过【默认插槽】发给我的数据,并把数据装进
slotProps这个变量里。”
二、拆开逐个解释(重点!)
1️⃣ #是什么?
# === v-slot:
✅ 只是简写
#default === v-slot:default
2️⃣ default是什么?
default表示 “没有名字的插槽”
|
子组件 |
父组件 |
|---|---|
|
|
|
✅ 只要是 没写 name 的插槽,都叫 default
3️⃣ ="slotProps"是什么?
👉 这是你定义的变量名
="slotProps"
意思是:
子组件传过来的所有数据
👉 全部放到
slotProps这个对象里
✅ 它不是 Vue 关键字
✅ 你可以随便改名
#default="data"
#default="scope"
#default="banana"
三、用最直白的代码对比(秒懂)
✅ 子组件(发数据的人)
<slot :row="user" :index="i"></slot>
✅ 父组件(收数据的人)
❌ 看不懂的写法
<template #default="slotProps">
{{ slotProps.row.name }}
</template>
✅ 改写成你熟悉的写法
<template v-slot:default="slotProps">
{{ slotProps.row.name }}
</template>
✅ 再简化成“解构写法”(最推荐 ✅)
<template #default="{ row, index }">
{{ row.name }}
</template>
👉 是不是瞬间清楚了?
四、用生活例子秒懂 🧠
📦 快递比喻
|
Vue 概念 |
生活 |
|---|---|
|
子组件 |
快递员 |
|
|
快递包裹 |
|
|
你签收快递 |
|
|
你拿到的快递箱 |
|
|
箱子里的东西 |
✅ 你不关心快递员
✅ 你只关心箱子里的东西
五、常见错误(你一定会遇到 ⚠️)
❌ 错误 1:忘了写 template
<Child #default="slotProps"></Child> ❌
✅ 正确:
<Child>
<template #default="slotProps"></template>
</Child>
❌ 错误 2:以为 slotProps是固定的
#default="slotProps" ❌(名字不能乱猜)
✅ 正确:
#default="data"
六、终极总结(背下来 ✅)
🔹
#=v-slot:🔹
default= 默认插槽🔹
slotProps= 随便起的变量名🔹 子组件发数据,父组件收数据
最推荐写法:
<Child v-slot="{ row }">
{{ row.name }}
</Child>
如果你愿意,我可以 用你现在做的后台 Table 页面,直接帮你写一个 100% 可用的作用域插槽实战代码,一看就懂 👍


这两张图片展示了 Vue 作用域插槽(Scoped Slots) 中“多个具名插槽同时传递数据”的完整实战写法。
这是 Vue 2.6+ 和 Vue 3 中非常经典且常用的模式。我来帮你把图片中的代码逻辑拆解清楚,特别是你可能存疑的 template结构。
1. 核心逻辑图解
这是一个 “子组件提供数据,父组件决定如何渲染” 的过程:
-
左边(
SlotsAttr.vue子组件):-
挖了两个坑:
name="header"和name="main"。 -
关键点:它不光挖坑,还往坑里扔了两个包裹(
msg和job)。-
header坑里扔了msg。 -
main坑里扔了job。
-
-
-
右边(
App.vue父组件):-
准备了两张网(
template),分别去接这两个坑的数据。 -
重点:注意看第 17 行和第 20 行,两张网是同时存在的,互不干扰。
-
2. 逐行代码揭秘
第一步:子组件挖坑并扔包裹
<!-- SlotsAttr.vue -->
<slot name="header" :msg="childMessage"></slot>
<!-- 含义:在 header 这个位置,把 childMessage 的值传给外面,外面要用 msg 这个名字接收 -->
(注:图中第二行 name="mian"应该是拼写错误,应为 main)
第二步:父组件用双网接包
这是图二中最核心的部分,展示了如何同时处理两个插槽的数据:
<SlotsAttr>
<!-- 第一张网:专门接 header 的数据 -->
<template #header="slotProps">
<h3>{{ currentTest }}-{{ slotProps.msg }}</h3>
</template>
<!-- 第二张网:专门接 main 的数据 -->
<!-- 注意:即使有两个插槽,每个 template 依然可以独立写 v-slot -->
<template #main="slotProps">
{{ slotPros.job }}
<!-- ⚠️注意:图中这里写成了 slotPros (多了个s),应该是笔误,正确是 slotProps -->
</template>
</SlotsAttr>
3. 为什么要把代码写在 <template>里?
你可能会疑惑,为什么不把 #header直接写在 <div>上?
<!-- ❌ 错误的理解写法 -->
<div #header="slotProps">...</div>
原因如下:
-
#header只能出现在一个元素上:Vue 规定v-slot(即#)只能用在<template>上(或者组件上)。 -
保留原始 DOM 结构:如果你写在
<div>上,那个<div>就会成为实际 DOM 元素渲染出来。但很多时候我们只是想把数据包在 H3 或 P 标签里,不想多嵌套一层 div。<template>是一个虚拟标签,渲染后不会出现在页面结构中。
4. 图中的小彩蛋(Bug 预警 ⚠️)
仔细看图二的第 21 行:
{{ slotPros.job }}
这里的变量名拼错了!
-
上面定义的是
slotProps(第 17 行)。 -
下面用的却是
slotPros(多了一个 s)。
如果不修正,页面上是不会显示 jobMessage的。 这是一个很好的找错练习。
5. 进阶写法:缩写(Shorthand)
如果你觉得给每个插槽都起一个 slotProps太麻烦(因为它们互不冲突),Vue 允许你在使用时直接解构:
<SlotsAttr>
<!-- 接 header 的数据,并直接把 msg 解构出来 -->
<template #header="{ msg }">
<h3>{{ msg }}</h3>
</template>
<!-- 接 main 的数据,并直接把 job 解构出来 -->
<template #main="{ job }">
{{ job }}
</template>
</SlotsAttr>
这样写更清爽,直接拿到需要的变量,不用再通过 .号去取。
总结
这两张图教你的就是:当子组件有多个位置需要透传数据时,父组件就写多个 <template #名字="数据对象">,各管各的,互不打架。
posted on 2026-06-18 11:20 luzhouxiaoshuai 阅读(24) 评论(0) 收藏 举报
浙公网安备 33010602011771号