Vue3里的slot到底怎么用?从基础到进阶一次讲透
做Vue项目时,有没有遇到过“子组件的结构想让父组件自定义”的情况?比如写弹窗组件时,头部要放标题和关闭按钮,底部要放确认取消按钮;或者做通用列表时,每个列表项的渲染样式想交给父组件决定……这时候,slot(插槽) 就是解决这类“内容分发”问题的关键,今天咱们从基础到进阶,把Vue3的slot用法拆明白。
问题1:Vue3里的slot是干啥的?解决什么开发痛点?
简单说,slot是分发的工具——子组件提前在模板里留好“坑位”,父组件往这些“坑位”里塞内容(可以是文字、HTML标签、甚至其他组件)。
举个实际例子:做一个通用按钮组件<MyButton>,有的按钮需要左边加图标,有的只要文字,有的还要右边带小箭头,如果不用slot,得给<MyButton>加icon、arrow等一堆props,逻辑会臃肿到爆炸;但用slot的话,子组件里只需要留个<slot></slot>,父组件用的时候直接写<MyButton><Icon /><span>提交</span></MyButton>,想塞啥结构就塞啥,灵活度拉满。
问题2:最基础的“匿名slot”怎么用?
匿名slot是最简单的用法——子组件里只写一个<slot></slot>,父组件包裹的所有内容会默认塞进这个唯一的坑位。
子组件(MyButton.vue)代码:
<template>
<button class="my-btn">
<!-- 这里是匿名slot,父组件内容会渲染到这 -->
<slot></slot>
</button>
</template>
父组件中使用示例:
<template>
<MyButton>
<!-- 下面这些内容会被塞进子组件的slot里 -->
<Icon name="edit" />
<span>编辑</span>
</MyButton>
</template>
注意:一个子组件里只能有一个匿名slot,如果父组件塞了多段内容,所有内容都会挤到这一个slot里,这种用法适合结构简单的场景(比如按钮、提示框的“单块内容自定义”)。
问题3:多个区域要自定义,“具名slot”怎么玩?
如果子组件有多个“坑位”(比如弹窗的头部、主体、底部),就得用具名slot——给每个slot起个唯一名字,父组件精准“投喂”对应内容。
子组件(Modal.vue)代码:
<template>
<div class="modal">
<!-- 头部slot,名字叫header -->
<slot name="header"></slot>
<!-- 主体slot,名字省略时默认叫default -->
<slot></slot>
<!-- 底部slot,名字叫footer -->
<slot name="footer"></slot>
</div>
</template>
父组件中使用示例:
<template>
<Modal>
<!-- 用<template #名字> 精准匹配对应slot -->
<template #header>
<h2>弹窗标题</h2>
</template>
<!-- 匿名slot(name="default")直接写内容 -->
<p>这是弹窗主体内容...</p>
<template #footer>
<button>取消</button>
<button>确定</button>
</template>
</Modal>
</template>
这里是v-slot:的缩写语法,写法更简洁,每个具名slot可以理解为“带ID的坑位”,父组件用#名字精准匹配,完美解决“多区域自定义”的问题。
问题4:子组件的数据,父组件插槽怎么用?“作用域插槽”是关键!
有时候子组件里有数据(比如列表数组),但父组件想自定义数据的渲染方式,这时候得让子组件把数据“传给”父组件的插槽,这就是作用域插槽。
子组件(TodoList.vue)代码:
<template>
<ul class="todo-list">
<!-- 把todoList数据通过slot传出去,命名为list -->
<slot :list="todoList"></slot>
</ul>
</template>
<script setup>
import { ref } from 'vue'
const todoList = ref([
{ id: 1, text: '写代码', done: false },
{ id: 2, text: '测bug', done: true }
])
</script>
父组件中使用示例:
<template>
<TodoList>
<!-- #default="slotProps" 接收子组件传的参数 -->
<template #default="slotProps">
<li
v-for="todo in slotProps.list"
:key="todo.id"
>
<!-- 父组件自定义每个todo的样式 -->
<input type="checkbox" v-model="todo.done">
<span :style="{ textDecoration: todo.done ? 'line-through' : 'none' }">
{{ todo.text }}
</span>
</li>
</template>
</TodoList>
</template>
作用域插槽的核心是“子传父数据”:子组件在<slot>上通过绑定属性(比如list),父组件用#名字="变量"接收,变量里就包含子组件传的所有数据,这样父组件能完全控制数据的渲染逻辑,子组件只负责提供数据,组件解耦性更强。
问题5:插槽还能“动态切换”?Vue3的动态插槽怎么实现?
如果插槽的名字是变量(比如用户切换“详情页”或“列表页”,需要渲染不同slot),就用动态插槽,语法是v-slot:[变量名]。
子组件(DynamicComponent.vue)代码:
<template>
<div class="dynamic">
<slot name="tab1"></slot>
<slot name="tab2"></slot>
</div>
</template>
父组件中使用示例:
<template>
<div>
<button @click="activeTab = 'tab1'">显示Tab1</button>
<button @click="activeTab = 'tab2'">显示Tab2</button>
<DynamicComponent>
<!-- 用v-slot:[activeTab] 动态绑定插槽名 -->
<template v-slot:[activeTab]>
<p v-if="activeTab === 'tab1'">这是Tab1内容</p>
<p v-else>这是Tab2内容</p>
</template>
</DynamicComponent>
</div>
</template>
<script setup>
import { ref } from 'vue'
const activeTab = ref('tab1')
</script>
动态插槽让插槽的选择由变量控制,适合“多状态切换”“可配置化组件”场景(比如后台tabs切换、自定义布局切换)。
问题6:slot和props传内容,到底该咋选?
很多新手会混淆这俩,其实核心区别是传“结构”还是传“数据”:
- props:适合传“数据”(字符串、数字、对象等),子组件自己决定怎么渲染,比如给按钮传
type="primary",子组件用这个值加样式。 - slot:适合传“结构”(带HTML标签、组件的内容),父组件直接决定渲染的结构,比如给弹窗传整个头部的
<h2>标题</h2><Button />,子组件只负责“把内容放哪”。
举个直观对比:
- 用props做按钮图标:
<MyButton icon="edit" />,子组件里得写<Icon :name="icon" />。 - 用slot做按钮图标:
<MyButton><Icon name="edit" /></MyButton>,子组件直接渲染slot里的<Icon>。
结构复杂、需要父组件完全控制渲染的,用slot;简单数据传递,用props。
问题7:Vue3的slot和Vue2比,有啥不一样?
Vue3对slot做了语法简化和性能优化,核心变化有这些:
- v-slot缩写更简洁:Vue2里用
slot-scope接收作用域插槽数据,Vue3统一用v-slot:名字="变量",还能缩写成#名字="变量"。 - 动态插槽更友好:Vue3原生支持
v-slot:[变量],Vue2里得用不太直观的语法。 - 编译性能提升:Vue3的编译器对slot做了优化,渲染更高效(尤其是大量插槽的场景)。
如果是从Vue2转Vue3,只要记住“v-slot代替slot-scope,是v-slot:的缩写”,上手会非常快。
问题8:实际项目中,哪些场景必须用slot?
说几个高频场景,遇到这些需求直接想到slot:
- 弹窗/模态框组件、主体内容、底部按钮,完全由父组件自定义。
- 列表/表格组件:表格列的内容、列表项的渲染方式(todo 列表的每个项),父组件自定义样式和交互。
- 布局组件:比如后台管理系统的
<Layout>,侧边栏菜单、主体内容、底部版权,由不同页面的父组件通过slot传入。 - 通用容器组件:比如卡片
<Card>,头部、操作区、脚注等区域,允许不同页面自定义内容。
举个真实项目例子:做电商商品列表时,每个商品卡片的“促销标签”“加入购物车按钮”需要不同页面自定义(首页卡片和详情页卡片结构不同),这时候给<GoodsCard>加多个slot(#tag、#action),父组件按需填充内容,组件复用性直接拉满。
最后小结
slot是Vue组件化开发里“内容定制化”的核心工具,从简单的匿名slot到复杂的作用域、动态slot,本质都是“父组件给子组件传结构,子组件负责位置”,掌握这些用法后,写组件时能更灵活解耦,再也不用为“父组件想改子组件里某块结构”而狂加props啦~ 下次遇到组件内容自定义的需求,先想想slot怎么用,效率直接起飞!
版权声明
本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。
code前端网


