当前位置: 首页 > news >正文

插槽 Slot

文章目录

  • 前言
  • 一、为什么需要插槽
    • 1.1 问题
    • 1.2 插槽的作用
  • 二、默认插槽
    • 2.1 基本用法
    • 2.2 默认内容
  • 三、具名插槽
    • 3.1 定义
    • 3.2 父组件使用
    • 3.3 简写规则
  • 四、作用域插槽
    • 4.1 定义
    • 4.2 父组件使用
    • 4.3 具名 + 作用域插槽
  • 五、应用场景
    • 5.1 通用卡片组件
    • 5.2 布局组件
    • 5.3 表格自定义列
    • 5.4 对话框组件
  • 六、Vue 2 vs Vue 3 语法
    • 6.1 变化对照
    • 6.2 注意事项
  • 七、面试聚焦
    • 7.1 插槽内容的编译作用域
    • 7.2 作用域插槽 vs 普通插槽
    • 7.3 Vue 3 移除 slot-scope
  • 八、易混淆点
  • 九、思考与练习
  • 总结

前言

插槽(Slot)是 Vue 的内容分发机制,让父组件可以向子组件模板中注入任意内容。本篇会讲清楚:

  • 默认插槽、具名插槽、作用域插槽
  • v-slot/#简写
  • 插槽内容的编译作用域
  • Vue 2 到 Vue 3 的语法变化

一、为什么需要插槽

1.1 问题

<!-- 子组件 Card 只能写死内容 --> <template> <div class="card"> <h3>固定标题</h3> <p>固定内容</p> </div> </template> <!-- 父组件无法自定义卡片内部内容 --> <Card />

1.2 插槽的作用

插槽允许父组件向子组件注入自定义内容,子组件只负责布局和容器,内容由父组件决定。

<!-- 子组件 Card.vue --> <template> <div class="card"> <slot>默认内容</slot> </div> </template> <!-- 父组件 --> <Card> <h3>自定义标题</h3> <p>自定义内容</p> </Card>

二、默认插槽

2.1 基本用法

<!-- 子组件 MyButton.vue --> <template> <button class="btn"> <slot>默认按钮文字</slot> </button> </template> <!-- 父组件 --> <MyButton>提交</MyButton> <!-- 渲染:<button class="btn">提交</button> --> <MyButton /> <!-- 渲染:<button class="btn">默认按钮文字</button> -->

2.2 默认内容

<slot>标签内的内容是后备内容,父组件未传入内容时显示:

<slot> <span>暂无内容</span> </slot>

三、具名插槽

3.1 定义

具名插槽通过name属性区分多个插槽,父组件使用v-slot:name#name指定目标。

<!-- 子组件 Layout.vue --> <template> <div class="layout"> <header> <slot name="header"></slot> </header> <main> <slot></slot> <!-- 默认插槽 --> </main> <footer> <slot name="footer"></slot> </footer> </div> </template>

3.2 父组件使用

<template> <Layout> <!-- 具名插槽:v-slot:header 或 #header --> <template #header> <h1>页面标题</h1> </template> <!-- 默认插槽 --> <p>主要内容区域</p> <template #footer> <p>© 2026 版权所有</p> </template> </Layout> </template>

3.3 简写规则

<!-- 完整写法 --> <template v-slot:header>标题</template> <!-- 简写(推荐) --> <template #header>标题</template> <!-- 默认插槽简写 --> <template #default>内容</template> <!-- 或直接写内容,省略 template --> <Layout>内容</Layout>

四、作用域插槽

4.1 定义

作用域插槽将子组件的数据通过 props 传递给父组件,让父组件控制渲染逻辑。

<!-- 子组件 List.vue --> <script setup> defineProps({ items: Array }) </script> <template> <ul> <li v-for="(item, index) in items" :key="item.id"> <!-- 向父组件暴露 item 数据 --> <slot :item="item" :index="index"> {{ item.name }} </slot> </li> </ul> </template>

4.2 父组件使用

<template> <!-- 接收子组件传递的数据 --> <List :items="list" v-slot="{ item }"> <b>{{ item.name }}</b> - {{ item.desc }} </List> <!-- 解构多个 prop --> <List :items="list" v-slot="{ item, index }"> {{ index + 1 }}. {{ item.name }} </List> </template>

4.3 具名 + 作用域插槽

<!-- 子组件 Table.vue --> <script setup> defineProps({ data: { type: Array, required: true }, columns: { type: Array, required: true } }) </script> <template> <table> <thead> <tr> <!-- 向 #header 插槽暴露 columns --> <slot name="header" :columns="columns"></slot> </tr> </thead> <tbody> <tr v-for="row in data" :key="row.id"> <!-- 向 #row 插槽暴露当前行数据 --> <slot name="row" :row="row"></slot> </tr> </tbody> </table> </template>
<!-- 父组件 --> <script setup> const users = [ { id: 1, name: 'Alice', email: 'alice@example.com' }, { id: 2, name: 'Bob', email: 'bob@example.com' } ] const columns = ['姓名', '邮箱'] </script> <template> <Table :data="users" :columns="columns"> <template #header="{ columns }"> <th v-for="col in columns" :key="col">{{ col }}</th> </template> <template #row="{ row }"> <td>{{ row.name }}</td> <td>{{ row.email }}</td> </template> </Table> </template>

说明:

  • 子组件通过defineProps接收datacolumns
  • #header插槽接收子组件传出的columns,渲染表头
  • #row插槽在每一行循环中接收row,渲染单元格

五、应用场景

5.1 通用卡片组件

<!-- Card.vue --> <template> <div class="card"> <div class="card-header"> <slot name="header">默认标题</slot> </div> <div class="card-body"> <slot>默认内容</slot> </div> </div> </template> <!-- 使用 --> <Card> <template #header>用户信息</template> <p>姓名:Alice</p> </Card>

5.2 布局组件

<!-- PageLayout.vue --> <template> <div class="page"> <aside><slot name="sidebar" /></aside> <main><slot /></main> </div> </template>

5.3 表格自定义列

<!-- DataTable.vue --> <template> <table> <tr v-for="row in rows" :key="row.id"> <td v-for="col in columns" :key="col.key"> <slot :name="col.key" :row="row" :value="row[col.key]"> {{ row[col.key] }} </slot> </td> </tr> </table> </template> <!-- 父组件自定义某列渲染 --> <DataTable :rows="data" :columns="cols"> <template #status="{ value }"> <span :class="value">{{ value }}</span> </template> </DataTable>

5.4 对话框组件

<!-- Dialog.vue --> <template> <div class="dialog"> <div class="dialog-header"> <slot name="title">提示</slot> </div> <div class="dialog-body"> <slot /> </div> <div class="dialog-footer"> <slot name="footer"> <button @click="$emit('close')">关闭</button> </slot> </div> </div> </template>

六、Vue 2 vs Vue 3 语法

6.1 变化对照

Vue 2Vue 3
slot="header"#headerv-slot:header
slot-scope="{ item }"#default="{ item }"
具名 + 作用域分开写统一用v-slot
<!-- Vue 2 --> <List :items="list"> <template slot="item" slot-scope="{ item }"> {{ item.name }} </template> </List> <!-- Vue 3 --> <List :items="list"> <template #item="{ item }"> {{ item.name }} </template> </List>

6.2 注意事项

<!-- ❌ v-slot 不能直接用在普通元素上 --> <div #header>标题</div> <!-- ✅ 必须用在 template 上 --> <template #header>标题</template> <!-- ✅ 默认插槽可以省略 template --> <MyComponent>直接写内容</MyComponent>

七、面试聚焦

7.1 插槽内容的编译作用域

<!-- 插槽内容在父组件作用域中编译 --> <script setup> const parentMsg = '来自父组件' </script> <template> <Child> {{ parentMsg }} <!-- ✅ 可以访问父组件数据 --> <!-- {{ childMsg }} ❌ 无法访问子组件数据(除非作用域插槽) --> </Child> </template>

7.2 作用域插槽 vs 普通插槽

// 普通插槽:父组件向子组件注入内容// 作用域插槽:子组件向父组件暴露数据,父组件决定如何渲染// 典型场景:表格/List 组件暴露 row 数据,父组件自定义列渲染

7.3 Vue 3 移除 slot-scope

// Vue 2: slot + slot-scope 两个属性// Vue 3: 统一 v-slot(简写 #),语法更一致

八、易混淆点

  1. 插槽内容在父组件作用域编译:只能访问父组件的数据和方法,不能直接访问子组件内部数据。
  2. 作用域插槽的数据是只读的:父组件不能通过插槽 props 修改子组件内部状态。
  3. v-slot 只能用在<template>:默认插槽内容可以直接写,具名/作用域插槽需用 template。
  4. Vue 3 移除 slot / slot-scope:统一使用v-slot#简写。
  5. 默认插槽名是 default<slot>等价于<slot name="default">

九、思考与练习

1.插槽和普通 Props 传递 HTML 有什么区别?

解析:

  • Props:传递数据,子组件控制如何渲染
  • 插槽:传递内容/结构,父组件控制渲染什么

2.作用域插槽和普通插槽的区别?

解析:

  • 普通插槽:父 → 子,父组件注入内容
  • 作用域插槽:子 → 父,子组件暴露数据,父组件决定渲染方式

3.插槽内容在哪个作用域编译?

解析:在父组件作用域编译,只能访问父组件的数据和方法。需要子组件数据时使用作用域插槽。

4.Vue 3 中如何写具名作用域插槽?

<Child v-slot:item="{ row }"> {{ row.name }} </Child> <!-- 或 --> <Child #item="{ row }"> {{ row.name }} </Child>

5.为什么 v-slot 不能直接用在 div 等元素上?

解析:v-slot是编译时指令,用于标记插槽内容边界。Vue 3 要求它用在<template>上,以便编译器正确识别插槽边界;默认插槽的内容可以直接写在组件标签内。


总结

  • 插槽:Vue 的内容分发机制,父组件向子组件注入内容
  • 默认插槽<slot>,父组件直接写内容
  • 具名插槽<slot name="xxx">,父组件用#xxx指定
  • 作用域插槽:子组件通过<slot :prop="val">暴露数据,父组件自定义渲染
  • 编译作用域:插槽内容在父组件作用域编译
  • Vue 3:统一v-slot/#,移除 slot-scope
http://www.jsqmd.com/news/1030583/

相关文章:

  • 2026年重庆政企单位驻点安保合规指南与品牌深度横评;保安派遣服务怎么选? - 年度推荐企业名录
  • Windows平台快速安装苹果苹方字体:完整指南与实用技巧
  • 2026武汉高端腕表回收测评|宇舶格拉苏蒂肖邦变现品牌排行 - 名奢变现站
  • 如何永久保存微信聊天记录:你的数字记忆守护者终极指南
  • Video2X:三步免费让模糊视频变4K超清,AI智能放大真的这么简单?
  • 2026金属阻尼隔声板厂家|金属消音板厂家推荐:福源来领衔,金属穿孔隔声板/机制吸隔声板源头厂家一站式供货选购指南 - 栗子测评
  • 如何规划航摄任务:从分区基准面到航线布设的完整参数推演
  • 成都闲置黄金如何稳妥变现?五大正规回收机构中立测评公示 - 讯息早知道
  • ZigBee 3.0 简单计量集群开发指南:从核心API到低功耗抄表实践
  • 2026天津名表回收门店实力排名|全域可上门变现首选禹竞名奢汇 - 名奢变现站
  • 宁波爱彼皇家橡树回收,实时对标国际行情 - 逸程
  • 2026海勃湾商铺酒店设备回收测评 - LYL仔仔
  • 深入解析msmarco-distilbert-base-v4:DistilBERT在MSMARCO数据集上的优化指南
  • ZigBee智能能源价格簇API实战:从原理到物联网设备数据同步应用
  • 柔性上料载带机哪家好
  • 基于白鹭群优化算法ESOA的多无人机协同集群避障路径规划算法研究,目标函数:最低成本:路径、高度、威胁、转角附Matlab代码
  • Japanese-MPT-7B应用案例:日语客服、翻译、创作的实战演示
  • 2026年精密齿轮供应商怎么选?厂家综合实力对比分析 - GrowthUME
  • 从零开始:如何用Go语言在5分钟内创建专业级Windows桌面应用?
  • 青岛市北区黄金回收抢先出价!合扬捷足先登,抢占市场高价先机 - 奢侈品交易观察员
  • 2026硚口区靠谱装修公司排名:本土老牌整装优选,自有工地展厅适配新房老房别墅 - 品牌优企推荐
  • 2026年6月常州黄金回收实测,黄金协会认证门店,报价透明不压价 - 薛定谔的梨花猫
  • 黄金回收怎么选?北京6家主流平台,多维对比解析! - 奢侈品回收测评
  • 深度解析Electron应用构建:企业级drawio-desktop自动化打包实战指南
  • ZigBee 3.0协议栈开发实战:从网络架构到安全机制的深度解析
  • 2026年重庆保安派遣与驻点安保服务深度横评:如何选择正规合规的专业安保团队 - 年度推荐企业名录
  • 旧安卓手机变Kali渗透测试环境:Termux免Root部署与实战指南
  • palera1n越狱实战:快速上手iOS设备越狱完整指南
  • 从矩阵指数到动态系统:一阶常系数微分方程组的工程实践
  • 惊!这些海使型商家现货超多,究竟藏着怎样的供货秘诀? - 信息热点