组件通信-作用域插槽

组件通信-作用域插槽

这是插槽最强大的地方。有时候,父组件提供的插槽内容需要依赖于子组件内部的数据(例如列表组件需要让父组件自定义每一项如何渲染)。

实现原理:子组件在渲染 <slot> 时,通过属性的形式将数据绑定到标签上,父组件通过 v-slot 接收这些属性。

实例展示

例如我们现在有两个组件,一个组件是Menu.vue,这个组件负责管理菜单数据,但是这个组件不决定每个菜品如何展示

vue 复制代码
<template>
    <div class="menu">
        <h3>今日菜单</h3>
    </div>
</template>

<script setup lang="ts" name="menu">
import { ref } from 'vue'

// 模拟菜单数据
const menuItems = ref([
    { id: 1, name: '宫保鸡丁', price: 48, spicy: true, vegan: false },
    { id: 2, name: '麻婆豆腐', price: 28, spicy: true, vegan: true },
    { id: 3, name: '清炒时蔬', price: 22, spicy: false, vegan: true },
    { id: 4, name: '水煮鱼', price: 88, spicy: true, vegan: false }
])</script>

<style scoped></style>
  • 还有一个父组件
vue 复制代码
<template>
  <Menu />
</template>

<script setup lang="ts">
import Menu from './Menu.vue';
</script>

<style scoped></style>
  • 然后我们需要将这些菜单数据传给父组件,然后让父组件去决定这个数据如何进行展示
vue 复制代码
<template>
    <div class="menu">
        <h3>今日菜单</h3>
        <ul>
            <li v-for="(item, idx) in menuItems" :key="item.id">
                <slot name="dish" :dish="item" :index="idx"></slot>
            </li>
        </ul>
    </div>
</template>

:index="idx" 是将当前循环的索引值 idx 通过作用域插槽暴露给父组件,让父组件在自定义每个菜品展示时能够使用这个索引。例如在普通顾客视图中,父组件通过 {``{ index + 1 }} 显示菜品的序号(从 1 开始)。这样父组件就拥有了子组件内部循环的索引信息,增强了渲染的灵活性。

  • 既然子组件给我们传了,那我们就收就完事了
vue 复制代码
 <Menu>
      <template #dish="{ dish, index }">
        <div class="customer-view">
          <span class="index">{{ index + 1 }}.</span>
          <strong>{{ dish.name }}</strong>
          <span class="price">¥{{ dish.price }}</span>
          <span v-if="dish.spicy" class="spicy">🌶️ 辣</span>
        </div>
      </template>
    </Menu>

解析:首先我们要将数据解构出来,解构出来菜单数组和序号;然后进行关联就可以了

  • 这并不是证明父组件可以绝对这些数据的展示,例如我们有三种用户,一个是顾客,一个是厨师,一个是促销视图
vue 复制代码
<Menu>
      <template #dish="{ dish, index }">
        <div class="customer-view">
          <span class="index">{{ index + 1 }}.</span>
          <strong>{{ dish.name }}</strong>
          <span class="price">¥{{ dish.price }}</span>
          <span v-if="dish.spicy" class="spicy">🌶️ 辣</span>
        </div>
      </template>
    </Menu>

    <hr />

    <!-- 员工视图(后厨):只显示菜名和特殊要求(如辣、素) -->
    <Menu>
      <template #dish="{ dish }">
        <div class="kitchen-view">
          <span>{{ dish.name }}</span>
          <span v-if="dish.spicy" class="badge">🔥 辣</span>
          <span v-if="dish.vegan" class="badge">🥬 素食</span>
        </div>
      </template>
    </Menu>

    <hr />

    <!-- 促销视图:特价菜品(价格打五折) -->
    <Menu>
      <template #dish="{ dish }">
        <div class="promo-view">
          <span>{{ dish.name }}</span>
          <span class="old-price">¥{{ dish.price }}</span>
          <span class="new-price">¥{{ (dish.price * 0.5).toFixed(0) }}</span>
          <span class="tag">限时五折</span>
        </div>
      </template>
    </Menu>
  </div>
</template>
  • 可以加一点点的样式
vue 复制代码
.customer-view {
  padding: 8px;
  border-bottom: 1px solid #eee;
}

.index {
  margin-right: 8px;
  color: #666;
}

.price {
  margin-left: 12px;
  color: #e67e22;
  font-weight: bold;
}

.spicy {
  margin-left: 8px;
  color: red;
  font-size: 0.8em;
}

.kitchen-view {
  background: #f9f9f9;
  padding: 8px;
  margin-bottom: 4px;
  border-radius: 4px;
}

.badge {
  margin-left: 8px;
  background: #ffebcc;
  padding: 2px 6px;
  border-radius: 12px;
  font-size: 0.7em;
}

.promo-view {
  padding: 8px;
  background: #fff3e0;
  margin-bottom: 4px;
  border-radius: 4px;
}

.old-price {
  text-decoration: line-through;
  margin-left: 12px;
  color: #999;
  font-size: 0.9em;
}

.new-price {
  margin-left: 8px;
  color: #d35400;
  font-weight: bold;
  font-size: 1.1em;
}

.tag {
  margin-left: 12px;
  background: #e67e22;
  color: white;
  padding: 2px 6px;
  border-radius: 12px;
  font-size: 0.7em;
}
相关推荐
qq_3508972112 小时前
trea实用工具
vue.js·uniapp
paopaokaka_luck12 小时前
非遗文物数字化系统(AI非遗问答、ONNX图像识别、协同过滤推荐、ECharts数据分析、非遗知识浏览与互动、文创商城订单闭环、文化活动报名签到、社区交流)
前端·javascript·vue.js·人工智能·数据分析·echarts
乘风gg12 小时前
AI Coding 提效 2 倍是真的吗?到底怎么衡量效果
前端·ai编程·claude
用户9210802628612 小时前
从对象到原型链:理解 this、构造函数和 new
前端
猫不易12 小时前
Webpack 与 Vite:从 Loader / Plugin 到 Rolldown 统一引擎
前端·vite
YHL13 小时前
🎯 Danci —— 用 AI 驱动开发一个全栈英语单词学习平台
前端·后端
平头哥~13 小时前
Day 20 _ 3D 翻卡_perspective 写错人,卡片就穿帮
前端·3d·css3·学习资料
小聪70813 小时前
elpis-core 前端 Webpack 工程化实践
前端
2601_9539880713 小时前
Ricon组态实时监控 - 毫秒级数据可视化
前端·物联网·数学建模·信息可视化·架构·前端框架
mONESY13 小时前
让大模型「听话」输出 JSON:LangChain OutputParser 结构化输出实战,从手写正则到 Zod Schema 四层进化
javascript