从入门到精通:Vue 插槽(slot)完全指南,让组件复用率直接拉满

5 阅读13分钟

从入门到精通:Vue 插槽(slot)完全指南,让组件复用率直接拉满

在 Vue 组件开发中,我们常常会遇到这样的痛点:写了一个通用的卡片组件、弹窗组件、列表组件,却因为内部内容不通用,每次都要改组件源码,或者写一堆重复的 props 来控制显示内容。而 slot(插槽) ,就是 Vue 为解决这个问题设计的「终极武器」—— 它让组件变成了「可插拔的模板」,父组件可以自由向子组件传递任意模板内容,实现极致的复用性和灵活性。

今天这篇博客,我们就从基础到进阶,彻底搞懂 Vue 插槽的所有用法,从默认插槽、具名插槽到作用域插槽,搭配实战案例,让你看完就能直接用在项目里!

一、slot 到底是什么?一句话讲透核心本质

插槽(slot),本质是组件内预留的「占位符」,让父组件可以向子组件传递自定义的模板内容,子组件只负责布局和逻辑,内容由父组件决定。

举个最直观的例子:我们做一个通用的「卡片组件」,卡片的标题、按钮、内容区都是可变的。如果不用插槽,我们需要给组件加一堆 props(titlebtnTextcontent 等),不仅代码臃肿,还无法传递复杂的模板(比如带样式的 HTML、其他组件)。

而用插槽,我们只需要在卡片组件里预留几个「坑位」,父组件用的时候,直接把自己的内容「填」进去就行,子组件完全不用关心内容是什么,只负责把布局做好,完美实现「一套组件,N 种内容」。

简单来说,插槽解决了两个核心问题:

  1. 组件复用:一套通用布局,适配不同内容,不用重复写组件
  2. 内容灵活:支持传递任意模板(HTML、组件、指令等),远超 props 的能力

二、基础用法:默认插槽,快速上手

默认插槽是最基础的插槽,也是我们最常用的一种,适合子组件只有一个内容占位的场景。Vue 中的插槽机制灵感来源于原生 Web Component 的 元素,同时做了功能拓展,让使用更灵活。

1. 子组件:定义插槽占位

我们先写一个通用的「按钮组容器」组件BtnGroup.vue,只负责布局,按钮内容由父组件传递:

<!-- src/components/BtnGroup.vue -->
<template>
  <div class="btn-group">
    <!-- 这里就是默认插槽的占位符(插槽出口),父组件传递的内容会渲染在这里 -->
    <slot></slot>
  </div>
</template>

<style scoped>
.btn-group {
  display: flex;
  gap: 12px;
  padding: 16px;
  background: #f5f5f5;
  border-radius: 8px;
}
</style>

这里的 就是默认插槽的「出口」,标示了父组件提供的「插槽内容」将在哪里被渲染,相当于在组件里挖了一个「坑」,等着父组件来填。

2. 父组件:填充插槽内容

父组件使用 BtnGroup 时,直接在子组件标签内部写内容,这些内容就会自动渲染到子组件的 位置。插槽内容可以是任意合法的模板内容,不局限于文本,还可以是多个元素、组件甚至指令:

<!-- 父组件 -->
<template>
  <div class="page">
    <!-- 用法1:传递普通按钮(简单文本+基础样式) -->
    <BtnGroup>
      <button class="btn-primary">确认提交</button>
      <button class="btn-default">取消操作</button>
    </BtnGroup>

    <!-- 用法2:传递带图标、组件的复杂内容 -->
    <BtnGroup>
      <a-button type="primary" ghost>
        <HomeOutlined />
        返回首页
      </a-button>
      <a-button type="danger">
        <DeleteOutlined />
        删除数据
      </a-button>
    </BtnGroup>
  </div>
</template>

<script lang="ts" setup>
import BtnGroup from '@/components/BtnGroup.vue'
import { HomeOutlined, DeleteOutlined } from '@ant-design/icons-vue'
</script>

你会发现,同一个 BtnGroup 组件,我们可以传递完全不同的按钮内容,甚至是 Ant Design Vue 的组件,子组件完全不用修改,复用率直接拉满!这就是插槽的核心优势之一——让组件更灵活、更具复用性。

3. 插槽的默认内容

如果父组件没有提供任何内容,我们可以为插槽指定默认内容,作为兜底显示。只需将默认内容写在 标签之间即可:

<!-- 子组件修改:给插槽加默认内容 -->
<template>
  <div class="btn-group">
    <slot>
      <!-- 父组件没传内容时,默认显示这两个按钮 -->
      <button>默认按钮1</button>
      <button>默认按钮2</button>
    </slot>
  </div>
</template>

这样一来,当父组件只使用 而不传递任何内容时,就会显示默认的两个按钮;如果传递了内容,显式提供的内容会取代默认内容。

三、进阶用法:具名插槽,多占位精准控制

当子组件有多个内容占位时,默认插槽就不够用了。这时我们需要具名插槽——给每个插槽起一个唯一的名字,父组件可以精准地将内容填充到对应的位置,避免内容错乱。

1. 子组件:定义多个具名插槽

我们写一个通用的「页面头部」组件 PageHeader.vue,包含「标题区」「操作区」「副标题区」三个占位,给每个插槽添加 name 属性来区分:

<!-- src/components/PageHeader.vue -->
<template>
  <div class="page-header">
    <!-- 标题区:具名插槽 name="title" -->
    <div class="title-wrap">
      <slot name="title"></slot>
    </div>
    <!-- 副标题区:具名插槽 name="subtitle" -->
    <div class="subtitle-wrap">
      <slot name="subtitle"></slot>
    </div>
    <!-- 操作区:具名插槽 name="actions" -->
    <div class="actions-wrap">
      <slot name="actions"></slot>
    </div>
  </div>
</template>

<style scoped>
.page-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 16px 24px;
  background: #fff;
  border-bottom: 1px solid #eee;
}
.title-wrap {
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.actions-wrap {
  display: flex;
  gap: 12px;
}
</style>

这类带 name 的插槽被称为具名插槽,没有提供 name 的 出口会隐式地命名为「default」(即默认插槽)。

2. 父组件:精准填充具名插槽

父组件使用时,需要用 <template v-slot:插槽名>(简写 #插槽名)来指定内容填充到哪个插槽,这种方式能精准对应子组件的每个占位:

<!-- 父组件 -->
<template>
  <div class="user-manage-page">
    <PageHeader>
      <!-- 填充 title 插槽:v-slot:title 简写为 #title -->
      <template #title>
        <h2>用户管理中心</h2>
      </template>

      <!-- 填充 subtitle 插槽 -->
      <template #subtitle>
        <span>当前在线人数:128</span>
      </template>

      <!-- 填充 actions 插槽 -->
      <template #actions>
        <a-button type="primary">新增用户</a-button>
        <a-button>批量导出</a-button>
      </template>
    </PageHeader>
  </div>
</template>

<script lang="ts" setup>
import PageHeader from '@/components/PageHeader.vue'
</script>

💡 关键语法说明:

  • v-slot:插槽名 是 Vue 官方语法,Vue 2.6+ 支持,用于指定插槽目标
  • #插槽名v-slot: 的简写,Vue 3 推荐使用,更简洁高效
  • 没有指定 v-slot 的内容,会默认填充到默认插槽(如果子组件有默认插槽的话)

补充:当一个组件同时接收默认插槽和具名插槽时,所有位于顶级的非