Vue基础教程(121)组件和组合API之插槽基本用法:Vue组件插槽全解密:从“占位符”到“超能力道具”的魔法之旅

一、为什么说插槽是组件的“灵魂伴侣”?

想象一下:你设计了一个万能机器人(组件),能跑能跳能发光,但用户突然想给它加个“炒菜功能”… 如果没有插槽,你只能哭着重写代码。而插槽就像机器人身上的万能接口,允许别人插入任意内容,让组件从“霸道总裁”变成“贴心暖男”。

实际开发中,我曾被一个需求暴击:要做一个统一风格的弹窗,但有的弹窗要放表单,有的要放视频,还有的要放游戏机模拟器(客户真的提过!)。如果每个弹窗都写死内容,代码会变成一坨意大利面。直到我发现了插槽的魔法……

二、基础插槽:给组件开个“任意门”

1. 默认插槽:最简单的“占位符”
<!-- 组件:MagicCard.vue -->
<template>
  <div class="card">
    <h3>我是卡片的标题</h3>
    <slot></slot>  <!-- 这就是那个“任意门” -->
    <button>确定</button>
  </div>
</template>
<!-- 使用方式 -->
<MagicCard>
  <p>这里可以塞入任何内容!</p>
  <img src="cat.jpg" alt="甚至是一只猫">
</MagicCard>

效果相当于:

<div class="card">
  <h3>我是卡片的标题</h3>
  <p>这里可以塞入任何内容!</p>
  <img src="cat.jpg" alt="甚至是一只猫">
  <button>确定</button>
</div>

实战场景:比如做电商网站的商品卡片,同一款式却能显示衣服、手机、冰箱等完全不同内容。

2. 默认内容:插槽的“B计划”
<slot>默认内容:暂无数据~</slot>

这样当没人传递内容时,组件也不会“裸奔”。

三、具名插槽:给多个插槽发“身份证”

当你的组件需要多个插槽时,就得给它们起名字,就像酒店的前台需要知道哪个包裹送到哪个房间。

<!-- 组件:UserProfile.vue -->
<template>
  <div class="profile">
    <header>
      <slot name="header"></slot>
    </header>
    <main>
      <slot></slot>  <!-- 匿名插槽,自动获得名字“default” -->
    </main>
    <footer>
      <slot name="footer"></slot>
    </footer>
  </div>
</template>
两种“送货方式”:

方式一:传统快递(template + v-slot)

<UserProfile>
  <template v-slot:header>
    <h1>用户昵称:Vue大神</h1>
  </template>
  
  <template v-slot:default>
    <p>这里是主体内容</p>
  </template>
  
  <template v-slot:footer>
    <button>关注</button>
    <button>打赏</button>
  </template>
</UserProfile>

方式二:闪送服务(#语法糖)

<UserProfile>
  <template #header>
    <h1>用户昵称:Vue大神</h1>
  </template>
  
  <template #default>
    <p>这里是主体内容</p>
  </template>
  
  <template #footer>
    <button>关注</button>
    <button>打赏</button>
  </template>
</UserProfile>

真实案例:我司后台管理系统侧边栏,通过具名插槽区分logo区域、菜单区域、用户信息区域,不同项目只需替换对应部分。

四、作用域插槽:组件内部的“对外快递”

这是插槽的进阶玩法!允许子组件把数据“打包”传递给父组件,就像饭店允许顾客定制菜品。

<!-- 组件:TodoList.vue -->
<template>
  <ul>
    <li v-for="todo in todos" :key="todo.id">
      <!-- 把todo数据“快递”出去 -->
      <slot :todo="todo" :index="index"></slot>
    </li>
  </ul>
</template>
<script setup>
const todos = [
  { id: 1, text: '学习Vue', done: true },
  { id: 2, text: '写项目', done: false }
]
</script>

接收快递的两种姿势

<!-- 父组件中使用 -->
<TodoList v-slot="slotProps">
  <span :class="{ done: slotProps.todo.done }">
    {{ slotProps.index + 1 }}. {{ slotProps.todo.text }}
  </span>
</TodoList>
<!-- 或者解构赋值(更推荐) -->
<TodoList v-slot="{ todo, index }">
  <span :class="{ done: todo.done }">
    {{ index + 1 }}. {{ todo.text }}
  </span>
</TodoList>
具名作用域插槽:精准快递
<!-- 子组件 -->
<slot name="header" :user="userData"></slot>
<!-- 父组件 -->
<template #header="{ user }">
  <h1>欢迎,{{ user.name }}!</h1>
</template>

五、动态插槽名:像换“酒店房卡”一样灵活

<template>
  <div>
    <template v-for="item in slottedItems">
      <slot :name="item.slotName"></slot>
    </template>
  </div>
</template>

六、组合API中的插槽:新时代的玩法

在setup语法糖中,使用useSlots来访问插槽内容:

<script setup>
import { useSlots } from 'vue'

const slots = useSlots()

// 判断某个插槽是否有内容
if (slots.header) {
  console.log('header插槽有内容送达!')
}
</script>

七、实战演练:搭建一个“外卖订单组件”

假设我们要做一个支持多种商家模板的订单组件:

<!-- 组件:OrderCard.vue -->
<template>
  <div class="order-card">
    <div class="store-info">
      <slot name="store" :store="order.store"></slot>
    </div>
    
    <div class="items">
      <slot :items="order.items" :total="order.total"></slot>
    </div>
    
    <div class="actions">
      <slot name="actions" :order="order"></slot>
    </div>
  </div>
</template>
<script setup>
const order = {
  store: { name: '肯德基', rating: 4.8 },
  items: [
    { name: '汉堡', price: 25, count: 2 },
    { name: '薯条', price: 12, count: 1 }
  ],
  total: 62
}
</script>

使用示例

<OrderCard>
  <template #store="{ store }">
    <h2>{{ store.name }}</h2>
    <span>评分:⭐{{ store.rating }}</span>
  </template>
  <template #default="{ items, total }">
    <div v-for="(item, index) in items" :key="index">
      {{ item.name }} × {{ item.count }} 
      <span class="price">¥{{ item.price * item.count }}</span>
    </div>
    <div class="total">总计:¥{{ total }}</div>
  </template>
  <template #actions="{ order }">
    <button @click="reorder(order)">再来一单</button>
    <button @click="complaint(order)">投诉商家</button>
  </template>
</OrderCard>

八、插槽的“避坑指南”

  1. 作用域问题:插槽内容在父组件作用域中编译,访问的是父组件数据
  2. 响应性:作用域插槽传递的数据保持响应式
  3. 性能优化:避免在插槽内进行复杂计算,必要时使用v-memo

九、创意用法:让脑洞大开

场景1:游戏技能栏组件

<SkillBar>
  <template #skill1>
    <FireSkill :mana="100" />
  </template>
  <template #skill2>
    <HealSkill :cd="5" />
  </template>
</SkillBar>

场景2:可配置的仪表盘

<Dashboard>
  <template #widget1>
    <SalesChart />
  </template>
  <template #widget2>
    <UserGrowth />
  </template>
</Dashboard>

总结

插槽就像是给Vue组件安装了乐高接口,让组件从“死板模板”变成“创意工坊”。记住这个进化路径:

  • 基础插槽 → “这里可以放东西”
  • 具名插槽 → “这个地方放A,那个地方放B”
  • 作用域插槽 → “我提供材料,你来发挥”

当你下次写组件时,先问自己:这个位置未来会不会有变化?如果答案是“可能”,那就毫不犹豫地用插槽!毕竟,谁不喜欢一个既能保持统一风格,又能千变万化的组件呢?

现在就去给你的组件加上插槽,让它们获得“超能力”吧!

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

值引力

持续创作,多谢支持!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值