一、前言

在 Vue 开发中,我们经常使用组件化思想来构建可复用的 UI 模块。但有时候,我们希望父组件能向子组件“注入”一些自定义内容,比如按钮文字、列表项、甚至是复杂的表单。

这时候,slot(插槽)就派上用场了!

📌 插槽是 Vue 实现“内容分发”(Content Distribution)的核心机制

本文将带你从零开始,全面掌握 Vue 中的三种插槽:

  • ✅ 默认插槽(Default Slot)
  • ✅ 具名插槽(Named Slot)
  • ✅ 作用域插槽(Scoped Slot)

并通过真实项目案例,让你真正理解插槽的强大与灵活。

二、什么是插槽(slot)?

1. 类比理解

想象一个“卡片组件”:

<Card>
  <h3>标题</h3>
  <p>这里是内容</p>
  <button>操作按钮</button>
</Card>

我们希望 Card 组件能“接收”父组件传入的这些内容,并把它们渲染到正确的位置。

插槽就是“预留的坑位”,父组件可以往这些“坑”里填内容。


2. 插槽的本质

  • 插槽是 Vue 提供的一种内容分发 API
  • 它允许父组件向子组件传递 HTML 模板片段
  • 子组件通过 <slot></slot> 标签定义“内容插入点”

💡 插槽不是 prop,它传递的是“结构”而非“数据”。

三、默认插槽(Default Slot)

最简单的插槽,用于传递单一内容。

1. 子组件:Card.vue

<template>
  <div class="card" style="border: 1px solid #ddd; padding: 20px; margin: 10px;">
    <slot></slot> <!-- 默认插槽:内容将插入到这里 -->
  </div>
</template>

<script>
export default {
  name: 'Card'
}
</script>

2. 父组件使用

<template>
  <Card>
    <h3>欢迎使用插槽</h3>
    <p>这是默认插槽的内容</p>
  </Card>
</template>

<script>
import Card from './components/Card.vue'
export default {
  components: { Card }
}
</script>

渲染结果

<div class="card">
  <h3>欢迎使用插槽</h3>
  <p>这是默认插槽的内容</p>
</div>

四、具名插槽(Named Slot)

当组件需要多个“内容插入点”时,使用具名插槽。

1. 子组件:Layout.vue

<template>
  <div class="layout">
    <header>
      <slot name="header"></slot>
    </header>
    <main>
      <slot></slot> <!-- 默认插槽 -->
    </main>
    <footer>
      <slot name="footer"></slot>
    </footer>
  </div>
</template>

<script>
export default {
  name: 'Layout'
}
</script>

<style>
.layout header, .layout footer {
  background: #f1f1f1;
  padding: 10px;
  text-align: center;
}
.layout main {
  padding: 20px;
}
</style>

2. 父组件使用

<template>
  <Layout>
    <!-- 具名插槽:使用 v-slot:header -->
    <template v-slot:header>
      <h1>网站标题</h1>
    </template>

    <!-- 默认插槽 -->
    <p>这里是主要内容区域...</p>

    <!-- 具名插槽:使用 v-slot:footer -->
    <template v-slot:footer>
      <small>© 2025 版权所有</small>
    </template>
  </Layout>
</template>

<script>
import Layout from './components/Layout.vue'
export default {
  components: { Layout }
}
</script>

💡 v-slot:header 也可以简写为 #header(推荐):

<template #header>
  <h1>网站标题</h1>
</template>

五、作用域插槽(Scoped Slot)—— 核心难点!

有时候,父组件想使用子组件内部的数据来渲染内容,这就是作用域插槽的用武之地。

1. 子组件:UserList.vue

<template>
  <ul>
    <li v-for="user in users" :key="user.id">
      <!-- 将 user 数据通过插槽传递出去 -->
      <slot :user="user" :index="index">
        <!-- 默认内容 -->
        {{ user.name }}
      </slot>
    </li>
  </ul>
</template>

<script>
export default {
  name: 'UserList',
  data() {
    return {
      users: [
        { id: 1, name: '张三', age: 25 },
        { id: 2, name: '李四', age: 30 },
        { id: 3, name: '王五', age: 28 }
      ]
    }
  }
}
</script>

2. 父组件:自定义渲染逻辑

<template>
  <UserList>
    <!-- 接收子组件传递的数据 -->
    <template v-slot:default="slotProps">
      <strong>{{ slotProps.user.name }}</strong>
      ({{ slotProps.user.age }}岁)
      - 第 {{ slotProps.index + 1 }} 位
    </template>
  </UserList>
</template>

<script>
import UserList from './components/UserList.vue'
export default {
  components: { UserList }
}
</script>

渲染结果

<ul>
  <li><strong>张三</strong> (25岁) - 第 1 位</li>
  <li><strong>李四</strong> (30岁) - 第 2 位</li>
  <li><strong>王五</strong> (28岁) - 第 3 位</li>
</ul>

🔍 关键点解析:

  • v-slot:default="slotProps":接收插槽传递的数据
  • slotProps 是一个对象,包含 { user, index }
  • 你也可以解构写法:v-slot="{ user, index }"

六、插槽的简写语法(推荐)

原写法 简写
v-slot:header #header
v-slot:default="data" #default="data" 或 v-slot="data"(默认插槽)

✅ 推荐使用 # 语法,更简洁:

<UserList>
  <template #default="{ user }">
    <span>{{ user.name }}</span>
  </template>
</UserList>

七、插槽的实际应用场景

场景 说明
UI 组件库 Button、Modal、Table 等组件通过插槽支持自定义内容
列表渲染 Table 组件使用作用域插槽自定义列内容
布局组件 Header、Sidebar、Footer 通过具名插槽构建页面结构
高阶组件 封装通用逻辑,通过插槽暴露定制能力

八、最佳实践与注意事项

✅ 推荐做法

  • 使用 # 语法替代 v-slot:xxx
  • 为插槽提供默认内容,提高组件健壮性
  • 作用域插槽中传递的数据尽量精简明确
  • 在组件文档中清晰说明插槽用法

❌ 避免踩坑

  • 不要在插槽中直接修改子组件数据
  • 注意插槽作用域,避免变量冲突
  • Vue 2 中 v-slot 只能用在 <template> 或组件上

九、Vue 2 vs Vue 3 插槽差异

特性 Vue 2 Vue 3
插槽语法 slot="xxx"slot-scope 统一为 v-slot
作用域插槽 slot-scope v-slot
兼容性 支持旧语法 仅支持 v-slot

📌 建议:统一使用 Vue 3 的 v-slot 语法,更规范。

十、总结

插槽类型 适用场景 语法
默认插槽 单一内容插入 <slot></slot>
具名插槽 多个插入点 v-slot:header 或 #header
作用域插槽 父组件使用子组件数据 v-slot="{ data }"

一句话总结

插槽 = 组件的“留白艺术”,让组件更灵活、更可复用。

十一、结语

感谢您的阅读!如果你有任何疑问或想要分享的经验,请在评论区留言交流!

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐