一、为什么说插槽是组件的“灵魂伴侣”?
想象一下:你设计了一个万能机器人(组件),能跑能跳能发光,但用户突然想给它加个“炒菜功能”… 如果没有插槽,你只能哭着重写代码。而插槽就像机器人身上的万能接口,允许别人插入任意内容,让组件从“霸道总裁”变成“贴心暖男”。
实际开发中,我曾被一个需求暴击:要做一个统一风格的弹窗,但有的弹窗要放表单,有的要放视频,还有的要放游戏机模拟器(客户真的提过!)。如果每个弹窗都写死内容,代码会变成一坨意大利面。直到我发现了插槽的魔法……
二、基础插槽:给组件开个“任意门”
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>
八、插槽的“避坑指南”
- 作用域问题:插槽内容在父组件作用域中编译,访问的是父组件数据
- 响应性:作用域插槽传递的数据保持响应式
- 性能优化:避免在插槽内进行复杂计算,必要时使用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”
- 作用域插槽 → “我提供材料,你来发挥”
当你下次写组件时,先问自己:这个位置未来会不会有变化?如果答案是“可能”,那就毫不犹豫地用插槽!毕竟,谁不喜欢一个既能保持统一风格,又能千变万化的组件呢?
现在就去给你的组件加上插槽,让它们获得“超能力”吧!

被折叠的 条评论
为什么被折叠?



