×

Vue3 组件插槽(Slots)

独孤求败 独孤求败 发表于2026-07-05 22:25:29 浏览23 评论0

抢沙发发表评论

在 Vue3 组件化开发中,Props 解决的是“父组件向子组件传递数据”的问题。

但是在实际开发中,经常会遇到这样的需求:

  • 按钮组件内部显示什么内容由外部决定
  • 弹窗标题和内容由外部决定
  • 表格列内容由外部决定
  • 卡片头部、主体、底部由外部决定
  • 列表项的展示方式由外部决定

如果仅靠 Props,就会导致组件设计越来越复杂。

因此 Vue 提供了一个非常重要的机制:

插槽(Slot

插槽可以理解为:

组件预留的位置,由使用组件的人决定具体显示什么内容。

插槽是 Vue 组件复用能力的重要组成部分,也是企业级组件库设计的核心技术之一。

本文将系统讲解 Vue3 插槽的全部知识点。


一、什么是插槽

先看一个按钮组件。

普通写法

子组件:

<template>
  <button class="btn">
    按钮
  </button>
</template>

使用:

<MyButton />

页面显示:

<button>按钮</button>

这种写法的问题是:

按钮内容被写死了。

无法实现:

登录
注册
提交
保存
删除

不同场景的复用。


使用插槽

子组件:

<template>
  <button class="btn">
    <slot></slot>
  </button>
</template>

父组件:

<MyButton>
  登录
</MyButton>

页面渲染:

<button>
  登录
</button>

继续使用:

<MyButton>
  保存
</MyButton>

页面渲染:

<button>
  保存
</button>

这样按钮组件就具备了更强的扩展能力。


二、插槽的本质

插槽本质上是:

内容分发机制

子组件负责布局。

父组件负责内容。

例如:

<MyButton>
  登录
</MyButton>

这里:

登录

属于插槽内容。

而:

<slot></slot>

属于插槽出口。

Vue 会把父组件传递的内容渲染到指定位置。


三、插槽可以传递什么内容

插槽不仅可以传递文本。

还可以传递:

HTML

<MyButton>
  <span>登录</span>
</MyButton>

多个元素

<MyButton>
  <span>登录</span>
  <span>注册</span>
</MyButton>

组件

<MyButton>
  <IconUser />
</MyButton>

混合内容

<MyButton>
  <IconUser />
  用户中心
</MyButton>

插槽支持任意合法模板内容。


四、默认插槽

没有指定名称的插槽称为:

默认插槽
Default Slot

例如:

<template>
  <slot></slot>
</template>

这是最常见的插槽形式。

使用:

<MyComponent>
  内容
</MyComponent>

对应:

<slot></slot>

五、插槽的作用域

很多人刚学插槽时容易产生误解。

例如:

父组件:

<script setup>
const message = "Hello"
</script>

<MyButton>
  {{ message }}
</MyButton>

可以正常显示:

Hello

因为插槽内容定义在父组件中。


但是:

子组件:

<script setup>
const title = "Vue3"
</script>

<slot></slot>

父组件:

<MyButton>
  {{ title }}
</MyButton>

会报错。

因为:

插槽内容只能访问父组件作用域
无法访问子组件作用域

插槽遵循 JavaScript 的词法作用域规则。


六、默认内容

很多时候希望:

如果用户没有传递插槽内容。

则显示默认内容。

例如:

<template>
  <button>
    <slot>
      提交
    </slot>
  </button>
</template>

使用:

<MyButton />

显示:

提交

如果:

<MyButton>
  保存
</MyButton>

则显示:

保存

外部内容会覆盖默认内容。


七、为什么需要具名插槽

假设开发一个页面布局组件:

Header
Main
Footer

如果只有一个插槽:

<slot></slot>

显然无法区分:

头部内容
主体内容
底部内容

因此需要:

具名插槽
Named Slot

八、具名插槽

子组件:

<template>

  <header>
    <slot name="header"></slot>
  </header>

  <main>
    <slot></slot>
  </main>

  <footer>
    <slot name="footer"></slot>
  </footer>

</template>

这里有:

header
default
footer

三个插槽。


父组件:

<BaseLayout>

  <template #header>
    页面头部
  </template>

  页面主体

  <template #footer>
    页面底部
  </template>

</BaseLayout>

页面渲染:

页面头部
页面主体
页面底部

九、v-slot 指令

Vue 使用:

v-slot

指定插槽内容。

完整写法:

<template v-slot:header>
</template>

简写:

<template #header>
</template>

企业项目中几乎都使用简写。


十、默认插槽简写

显式写法:

<template #default>

</template>

例如:

<BaseLayout>

  <template #default>
    页面主体
  </template>

</BaseLayout>

通常直接写:

<BaseLayout>
  页面主体
</BaseLayout>

即可。


十一、条件插槽

有时希望:

传了插槽才渲染区域
没传则不渲染

例如卡片组件:

<div v-if="$slots.header">
  <slot name="header"></slot>
</div>

如果父组件没有提供:

<template #header>
</template>

整个 Header 区域不会渲染。

这种写法在企业项目中非常常见。


十二、$slots 对象

Vue 会自动维护:

$slots

例如:

console.log($slots)

输出:

{
  defaultFunction,
  headerFunction,
  footerFunction
}

可以用于:

判断插槽是否存在
动态控制布局

十三、动态插槽名

Vue3 支持动态插槽。

子组件:

<slot :name="slotName"></slot>

父组件:

<template #[slotName]>
  内容
</template>

适用于:

动态表格
动态布局
动态表单

等高级场景。


十四、作用域插槽

这是 Vue 插槽中最重要的知识点。

很多人第一次学习时都会困惑。

先看问题。


子组件:

<script setup>

const users = [
  { id:1,name:"Tom" },
  { id:2,name:"Jack" }
]

</script>

希望父组件决定:

每条数据怎么显示

怎么办?


十五、作用域插槽解决的问题

子组件:

<ul>

  <li
    v-for="user in users"
    :key="user.id"
  >

    <slot
      :user="user"
    ></slot>

  </li>

</ul>

这里:

:user="user"

向插槽暴露数据。


父组件:

<UserList
  v-slot="slotProps"
>

  {{ slotProps.user.name }}

</UserList>

显示:

Tom
Jack

这就是作用域插槽。

本质:

子组件向父组件传递数据
父组件决定如何渲染

十六、解构作用域插槽

实际开发中常用解构。

例如:

<UserList
  v-slot="{ user }"
>

  {{ user.name }}

</UserList>

等价于:

slotProps.user

但更简洁。


十七、具名作用域插槽

可以结合具名插槽使用。

子组件:

<slot
  name="header"
  :title="title"
></slot>

父组件:

<template #header="{ title }">

  {{ title }}

</template>

这样:

指定区域
+
传递数据

同时实现。


十八、企业级开发中的插槽应用

自定义表格列

<Table>

  <template #action="{ row }">

    <a-button>
      编辑
    </a-button>

  </template>

</Table>

弹窗组件

<MyDialog>

  <template #header>
    删除确认
  </template>

  <template #default>
    是否删除当前数据
  </template>

  <template #footer>
    操作按钮
  </template>

</MyDialog>

卡片组件

<Card>

  <template #header>
    用户信息
  </template>

  用户内容

  <template #footer>
    更多操作
  </template>

</Card>

表单组件

<FormItem>

  <template #label>
    用户名称
  </template>

</FormItem>

数据列表组件

<DataList
  v-slot="{ item }"
>

  {{ item.name }}

</DataList>

十九、插槽与 Props 的区别

对比项
Props
Slot
数据方向
父传子
父传模板
传递内容
数据
模板内容
灵活性
较低
很高
使用场景
配置组件
自定义组件结构
企业使用频率
极高
极高

简单理解:

Props 传数据

Slots 传界面

二十、企业级开发最佳实践

简单配置使用 Props

例如:

<Button
  type="primary"
/>

复杂内容使用 Slot

例如:

<Card>

  任意内容

</Card>

多区域布局使用具名插槽

例如:

Header
Body
Footer

必须使用:

Named Slot

数据渲染使用作用域插槽

例如:

Table
List
Tree
Menu

全部优先使用:

Scoped Slot

总结

插槽是 Vue3 组件设计的核心能力之一,其本质是内容分发机制。

Vue3 插槽体系主要包含以下内容:

  • 默认插槽

  • 具名插槽

  • 默认内容

  • 条件插槽

  • 动态插槽

  • 作用域插槽

  • 具名作用域插槽


群贤毕至

访客