Vue基础教程(120)组件和组合API之插槽:Vue插槽魔法:让你的组件像乐高一样自由拼接!

一、开篇:当一个组件想“搞特殊”时…

朋友们,想象这个画面:你吭哧吭哧写了个<Button>组件,结果产品经理拍桌要求——这个按钮既要能放图标又要能显示加载动画!你内心OS:“难道要写<ButtonWithIcon><ButtonWithLoading>二十个组件吗?!”

停!是时候请出插槽(Slots)这个救场王了!
它就像给组件开了个“任意门”,门外的人可以往里塞任何内容——文本、图片、甚至另一个组件!举个栗子🌰:

<!-- 万能按钮组件 -->
<template>
  <button class="my-btn">
    <!-- 这里是插槽入口,等着被投喂内容 -->
    <slot></slot>
  </button>
</template>
<!-- 使用时的魔法时刻 -->
<MyButton>
  <img src="icon.png"> 点击抽奖! <!-- 随便塞内容! -->
</MyButton>

是不是瞬间想起小时候玩的乐高?插槽就是让组件从“预制水泥块”升级成“万能积木”的神器!


二、插槽三大门派:从青铜到王者的进阶之路

1. 默认插槽:组件里的“占位符”

最适合插槽萌新的基础款,相当于给组件预留了个内容黑洞

<!-- 组件:MessageBox.vue -->
<template>
  <div class="message-box">
    <h3>重要通知</h3>
    <div class="content">
      <slot></slot> <!-- 内容将出现在这里 -->
    </div>
    <footer>系统自动生成</footer>
  </div>
</template>
<!-- 使用示例 -->
<MessageBox>
  <!-- 下面这堆内容会精准注入插槽 -->
  <p>本周五公司将举办<span class="fun">猫咪选美大赛</span>!</p>
  <ul>
    <li>参赛选手:全体办公室猫猫</li>
    <li>奖品:全年免费猫罐头</li>
  </ul>
</MessageBox>

效果预览

💡 避坑指南:如果组件调用时没传内容,<slot>标签内的默认内容就会显示:

<slot>这里是默认提示文本,像备胎一样随时待命</slot>

2. 具名插槽:组件中的“分房间术”

当你的组件需要多个内容插入点时,就该具名插槽登场了!比如这个<BlogPost>组件:

<!-- 组件:BlogPost.vue -->
<template>
  <article class="blog-post">
    <header>
      <slot name="header"></slot>
    </header>
    
    <main>
      <slot></slot> <!-- 默认插槽 -->
    </main>
    
    <footer>
      <slot name="footer"></slot>
    </footer>
  </article>
</template>

使用时需要带着身份证(v-slot)对号入座

<BlogPost>
  <template v-slot:header>
    <h1>程序员生存指南</h1>
    <p>作者:代码界的段子手</p>
  </template>
  <!-- 默认插槽内容,不用套template -->
  <p>当你发现bug时的心理活动:这不可能→有点意思→我裂开了</p>
  <template v-slot:footer>
    <div class="post-stats">
      <span>点赞 1.3万</span>
      <span>收藏 8千</span>
    </div>
  </template>
</BlogPost>

语法糖警报v-slot:header 可以简写成 #header,键盘寿命直接+1秒!


3. 作用域插槽:最烧脑也最强大的“跨界快递”

这是插槽界的碟中谍——允许子组件向父组件传递数据!想象一下:子组件对父组件说:“内容你定,但数据我提供!”

经典场景:一个负责遍历数据,但渲染方式交给使用者的组件:

<!-- 组件:DataList.vue -->
<template>
  <ul>
    <li v-for="item in items" :key="item.id">
      <!-- 把item数据打包发给使用方 -->
      <slot :item="item" :index="index"></slot>
    </li>
  </ul>
</template>
<script setup>
defineProps(['items'])
</script>

使用时父组件就能接收数据并自由定制显示

<DataList :items="userList">
  <template v-slot="slotProps">
    <!-- 现在可以随意使用item数据啦 -->
    <div class="user-card">
      <Avatar :src="slotProps.item.avatar" />
      <span>{{ slotProps.item.name }} - 年龄{{ slotProps.item.age }}</span>
    </div>
  </template>
</DataList>

🎯 进阶技巧:解构赋值让代码更清爽:

<template #default="{ item, index }">
  <div>第{{ index + 1 }}名:{{ item.name }}</div>

三、组合API + 插槽 = 如虎添翼

在Vue 3的<script setup>里,插槽用起来更加丝滑:

<!-- 组件:Tabs.vue -->
<script setup>
import { ref } from 'vue'

const tabs = ref(['首页', '热榜', '话题'])
const activeTab = ref(0)

// 暴露状态给插槽使用
defineExpose({ activeTab })
</script>
<template>
  <div class="tabs">
    <nav>
      <button 
        v-for="(tab, index) in tabs" 
        :key="index"
        @click="activeTab = index"
      >
        {{ tab }}
      </button>
    </nav>
    <div class="tab-content">
      <slot :activeTab="activeTab"></slot>
    </div>
  </div>
</template>

使用时轻松获取内部状态:

<Tabs v-slot="{ activeTab }">
  <div v-if="activeTab === 0">推荐内容...</div>
  <div v-if="activeTab === 1">热门榜单...</div>
</Tabs>

四、实战演练:两大完整示例

示例1:可变服装模特组件(具名插槽经典案例)
<!-- OutfitModel.vue -->
<template>
  <div class="model">
    <div class="head">
      <slot name="head"></slot>
    </div>
    <div class="body">
      <slot name="body"></slot>
    </div>
    <div class="accessories">
      <slot name="accessories"></slot>
    </div>
  </div>
</template>
<!-- 使用 -->
<OutfitModel>
  <template #head>
    <img src="cowboy-hat.png" alt="牛仔帽">
  </template>
  
  <template #body>
    <img src="punk-jacket.png" alt="皮夹克">
  </template>
  
  <template #accessories>
    <img src="guitar.png" alt="吉他">
    <img src="dog.png" alt="宠物狗">
  </template>
</OutfitModel>
示例2:智能待办清单(作用域插槽实战)
<!-- TodoList.vue -->
<script setup>
import { ref } from 'vue'

const todos = ref([
  { id: 1, text: '学习Vue插槽', done: true },
  { id: 2, text: '写段子让代码有趣', done: false },
  { id: 3, text: '教会家里的猫用插槽', done: false }
])

const toggleTodo = (id) => {
  const todo = todos.value.find(t => t.id === id)
  if (todo) todo.done = !todo.done
}
</script>
<template>
  <div class="todo-list">
    <slot :todos="todos" :toggleTodo="toggleTodo"></slot>
  </div>
</template>

使用时完全自定义UI:

<TodoList v-slot="{ todos, toggleTodo }">
  <div class="custom-todos">
    <div 
      v-for="todo in todos" 
      :key="todo.id"
      @click="toggleTodo(todo.id)"
    >
      <!-- 完全自由的渲染方式 -->
      <span :class="['emoji', todo.done ? '✅' : '📝']"></span>
      <span :style="todo.done ? 'text-decoration: line-through' : ''">
        {{ todo.text }}
      </span>
    </div>
    
    <p>统计:{{ todos.filter(t => t.done).length }}/{{ todos.length }} 完成</p>
  </div>
</TodoList>

五、结语:插槽,让组件关系更健康

记住这个公式:
插槽 = 组件界的社交牛逼症
它让父子组件从“冷漠的数据传递”变成“默契的内容合作”

下次当你:

  • 需要组件容器化 → 默认插槽
  • 组件有多个内容区 → 具名插槽
  • 子组件数据父组件渲染 → 作用域插槽

现在就去给你的组件加上插槽超能力吧!毕竟,不会用插槽的Vue程序员,就像不会讲段子的脱口秀演员——代码少了一半灵魂🎉


彩蛋:在作用域插槽里使用组合API的computed,体验双倍快乐!

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

const formattedItems = computed(() => 
  props.items.map(item => ({ ...item, fullName: `${item.firstName} ${item.lastName}` }))
)
</script>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

值引力

持续创作,多谢支持!

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

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

打赏作者

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

抵扣说明:

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

余额充值