Vue 插槽(Slot)全解析:从 Vue2 到 Vue3 核心用法与案例

插槽 (Slot)是 Vue 中最强大的功能之一,它的核心作用是实现组件内容的分发,让子组件成为一个可定制、可复用的「容器」,父组件可以向子组件传递任意 HTML 结构、组件甚至逻辑,彻底解决了组件内容固化的问题。

一、插槽核心概念

简单理解:

  • 子组件:定义插槽位置(相当于预留一个「空位」)。
  • 父组件:向「空位」填充自定义内容(文本、HTML、组件都可以)。
  • 如果父组件不填充内容,子组件可以设置默认内容。

二、Vue3 插槽完整用法

Vue3 完全兼容 Vue2 插槽语法,废弃了旧版语法 ,统一使用更简洁的 v-slot 指令(简写 #),语法更直观、语义更清晰。

1. 默认插槽(匿名插槽)

适用场景:子组件只有一个需要分发的内容,无需指定名称。

子组件(Child.vue)

使用 <slot> 标签定义插槽,可设置默认内容:

javascript 复制代码
<template>
  <div class="child-box">
    <h3>子组件标题</h3>
    <!-- 默认插槽:父组件不传递内容时,显示默认值 -->
    <slot>我是插槽默认内容</slot>
  </div>
</template>

父组件使用

直接在子组件标签内写内容,会自动填充到默认插槽:

javascript 复制代码
<template>
  <Child>
    <!-- 填充到子组件默认插槽的内容 -->
    <p>我是父组件传递的自定义内容</p>
    <button>点击按钮</button>
  </Child>
</template>

<script setup>
// Vue3 组合式 API
import Child from './Child.vue'
</script>

2. 具名插槽

适用场景:子组件有多个需要分发的位置,通过名称区分插槽。

语法:

  • 子组件:<slot name="插槽名"></slot>
  • 父组件:#插槽名v-slot:插槽名

子组件(Layout.vue)

javascript 复制代码
<template>
  <div class="layout">
    <!-- 头部插槽 -->
    <header>
      <slot name="header"></slot>
    </header>

    <!-- 中间默认插槽 -->
    <main>
      <slot></slot>
    </main>

    <!-- 底部插槽 -->
    <footer>
      <slot name="footer"></slot>
    </footer>
  </div>
</template>

父组件使用

必须用 <template> 标签配合插槽名称指定内容位置:

javascript 复制代码
<template>
  <Layout>
    <!-- 填充 header 插槽:#header 是 v-slot:header 的简写 -->
    <template #header>
      <h1>页面标题</h1>
    </template>

    <!-- 填充默认插槽(可写 #default 或省略) -->
    <template #default>
      <p>页面主体内容</p>
    </template>

    <!-- 填充 footer 插槽 -->
    <template #footer>
      <span>版权信息 © 2025</span>
    </template>
  </Layout>
</template>

<script setup>
import Layout from './Layout.vue'
</script>

3. 作用域插槽

核心价值:子组件向父组件传递数据,父组件可以使用子组件的数据渲染自定义内容。

适用场景:列表组件、表格组件(子组件提供数据,父组件决定展示样式)。

子组件(List.vue)

通过 v-bind 把数据绑定到插槽上,暴露给父组件:

javascript 复制代码
<template>
  <div class="list">
    <slot 
      v-for="item in list" 
      :key="item.id"
      <!-- 子组件暴露数据:自定义名称="子组件数据" -->
      :item="item"
    >
    </slot>
  </div>
</template>

<script setup>
import { ref } from 'vue'
// 子组件内部数据
const list = ref([
  { id: 1, name: 'Vue3 教程' },
  { id: 2, name: '插槽实战' },
  { id: 3, name: '组合式 API' }
])
</script>

父组件使用

接收子组件传递的数据,语法:#插槽名="接收对象"

javascript 复制代码
<template>
  <List>
    <!-- 默认作用域插槽:scope 接收子组件传递的所有数据 -->
    <template #default="scope">
      <!-- 使用子组件的 item 数据 -->
      <div class="item">
        {{ scope.item.id }} - {{ scope.item.name }}
      </div>
    </template>
  </List>
</template>

解构写法(更简洁,推荐):

javascript 复制代码
<!-- 直接解构子组件传递的 item -->
<template #default="{ item }">
  <div class="item">{{ item.id }} - {{ item.name }}</div>
</template>
4. 动态插槽名

Vue3 支持动态绑定插槽名称,灵活性拉满:

javascript 复制代码
<template>
  <Layout>
    <!-- 动态插槽:slotName 是变量 -->
    <template #[slotName]>
      动态内容
    </template>
  </Layout>
</template>

<script setup>
import { ref } from 'vue'
const slotName = ref('header') // 可动态修改
</script>

三、Vue2 插槽用法(对比参考)

Vue2 分为旧语法和新语法(v-slot),Vue3 废弃了旧语法,只保留 v-slot 统一语法。

1. Vue2 默认插槽
javascript 复制代码
<!-- 子组件 -->
<slot>默认内容</slot>

<!-- 父组件 -->
<Child>
  父组件内容
</Child>
2. Vue2 具名插槽(旧语法 vs 新语法)

旧语法(废弃):slot="名称"

新语法(兼容):v-slot:名称(Vue 2.6+ 支持)

javascript 复制代码
<!-- 旧语法(不推荐,Vue3 已删除) -->
<template slot="header">
  头部内容
</template>

<!-- 新语法(和 Vue3 一致) -->
<template v-slot:header>
  头部内容
</template>
3. Vue2 作用域插槽(旧语法 vs 新语法)

旧语法(废弃):slot-scope

新语法(兼容):v-slot

javascript 复制代码
<!-- 旧语法 -->
<template slot="default" slot-scope="scope">
  {{ scope.item }}
</template>

<!-- 新语法(和 Vue3 一致) -->
<template v-slot:default="scope">
  {{ scope.item }}
</template>

四、Vue2 与 Vue3 插槽核心区别

特性 Vue2 Vue3
推荐语法 2.6+ 支持 v-slot,兼容旧语法 仅支持 v-slot(简写 #),废弃所有旧语法
旧语法 支持 slotslot-scope 完全废弃,不再支持
作用域插槽 必须写在 <template> 上(新语法) 规则一致,更简洁
动态插槽 支持 支持,语法更简洁
写法限制 无特殊限制 具名插槽必须写在 <template>

一句话总结:Vue3 插槽是 Vue2 新语法的精简版,去掉了冗余语法,统一使用 #插槽名,学习成本更低,代码更简洁。

五、插槽使用注意事项

  1. 具名插槽必须配合 <template> :除了默认插槽,具名插槽 / 作用域插槽都要写在 <template> 标签内。
  2. 默认插槽简写#default 可以省略,直接写内容。
  3. 数据作用域:父组件插槽内容只能访问父组件数据,子组件插槽内容只能访问子组件数据(作用域插槽除外)。
  4. 默认内容优先级:父组件传递内容 → 覆盖子组件默认内容;不传递 → 显示默认内容。

六、实战场景:封装通用卡片组件

用 Vue3 插槽封装一个高复用卡片组件,演示默认 + 具名 + 作用域插槽组合使用:

子组件(Card.vue)

javascript 复制代码
<template>
  <div class="card">
    <!-- 卡片头部 -->
    <div class="card-header">
      <slot name="header">默认标题</slot>
    </div>

    <!-- 卡片内容 -->
    <div class="card-body">
      <slot>默认内容</slot>
    </div>

    <!-- 卡片底部:作用域插槽 -->
    <div class="card-footer">
      <slot name="footer" :time="currentTime"></slot>
    </div>
  </div>
</template>

<script setup>
import { ref } from 'vue'
const currentTime = ref(new Date().toLocaleString())
</script>

父组件使用

javascript 复制代码
<template>
  <Card>
    <!-- 头部 -->
    <template #header>
      <h4>自定义卡片标题</h4>
    </template>

    <!-- 内容 -->
    <p>这是卡片的自定义主体内容</p>

    <!-- 底部:接收子组件时间 -->
    <template #footer="{ time }">
      <span>更新时间:{{ time }}</span>
    </template>
  </Card>
</template>

总结

  • 插槽本质:组件内容分发工具,让子组件支持自定义内容。
  • Vue3 核心语法 :默认插槽、#名称(具名插槽)、#名称="数据"(作用域插槽)。
  • 与 Vue2 差异 :废弃 slot/slot-scope 旧语法,统一使用 v-slot 简写 #,更简洁规范。
  • 最佳实践:封装通用组件(列表、弹窗、布局、卡片)必用插槽,是 Vue 组件化开发的核心技能。

个人速记:

具名插槽 子组件用<scope name="xx">接收,父组件<template #xx>插入

作用域插槽 子组件用<scope name="xx" :变量名="item">,父组件<template #xxx="{ 变量名 }"> <span>更新时间:{``{ 变量名 }}</span> </template>接收子组件变量

相关推荐
喵了几个咪1 小时前
基于 Nuxt 4 的现代 Headless CMS 前端:架构深度解析与二次开发指南
前端·架构
weixin_427771612 小时前
css加载顺序导致本地和线上样式不一致
前端·css
漂流瓶jz9 小时前
Webpack如何实现万物皆可import?loader的使用/配置/手写实践
前端·javascript·webpack
ZC跨境爬虫9 小时前
跟着 MDN 学CSS day_41:显式轨道、隐式网格与区域命名放置
前端·javascript·css·ui·交互
修己xj10 小时前
告别手动存图!这款叫 Fatkun 的浏览器插件,简直是素材收集神器
前端
袋鼠云数栈11 小时前
从前端到基础设施,ACOS 如何打通企业全链路可观测
运维·前端·人工智能·数据治理·数据智能
AskHarries11 小时前
系统提示词、开发者指令和用户输入的优先级
java·前端·数据库
Moment11 小时前
长上下文会最终杀死 Rag 吗?
前端·javascript·后端
qcx2312 小时前
【系统学AI】25 论文导读 ①:两篇改变 AI 的开山之作——Attention Is All You Need & ReAct
前端·人工智能·react.js·transformer