今天依旧对Vue进行学习与回顾 ,重点复盘了组件插槽 的核心用法,同时简单回顾了"兄弟"组件通信的关键方式,整理成笔记方便后续复习巩固以及希望帮助到正在深耕于vue的你们~
一、兄弟组件通信
Vue中兄弟组件无法直接传递数据,今天介绍两种简单的实现方式:
1. 父组件中转(Emits + Props)
兄弟组件1通过**defineEmits** 触发自定义事件传数据,父组件接收后,再通过**Props**把数据传递给兄弟组件2。
代码示例:
TypeScript
<!-- 兄弟1:触发事件传值 -->
<script setup lang="ts">
const emit = defineEmits(["rev"])
function sendB2(){
emit("rev","hello") // 触发事件并传递数据
}
</script>
<!-- 兄弟2:接收父组件Props -->
<script setup lang="ts">
interface p{ msg:string }
const props = defineProps<p>()
</script>
<template>{{ props.msg }}</template>
<!-- 父组件:接受兄弟1的值并发送给兄弟2 -->
<script setup lang="ts">
import { ref } from "vue";
import b1 from "./study_tree/16.兄弟组件通信/18.bro1.vue"
import b2 from "./study_tree/16.兄弟组件通信/18.bro2.vue"
const b1Msg = ref("")
function receive(data:string){
b1Msg.value = data
}
</script>
<template>
<div>
<b1 @rev="receive"></b1>
<b2 :msg="b1Msg"></b2>
</div>
</template>
2. Event Bus 事件总线
兄弟 1 通过**emit** 发布事件,兄弟 2 通过**on**监听事件,无需父组件介入。
代码示例:
TypeScript
<!-- 兄弟1:发布事件 -->
<script setup lang="ts">
import bus from '@/until/bus';
import { ref } from 'vue';
const data = ref("")
function send(data:string){
bus.emit("transfer",data)
}
</script>
<!-- 兄弟2:监听事件 -->
<script setup lang="ts">
import bus from "@/until/bus"
import { ref } from "vue";
const result = ref("")
bus.on("transfer",(data:string)=>{
result.value = data
})
</script>
二、组件插槽(核心重点)
插槽是Vue实现组件内容灵活分发的核心能力,允许父组件向子组件传递任意结构的内容,结合实操代码拆解 3 类核心插槽:
1. 默认插槽(匿名插槽)
核心逻辑 :子组件用**<slot>**标记占位(可设默认值),父组件使用子组件时,直接写入内容即可替换默认值。
子组件代码:
TypeScript
<script setup lang="ts"></script>
<template>
<div>这是组件的内容 下面是需要替换的内容</div>
<!-- 插槽占位,设置默认值 -->
<div><slot>我是要被替换的</slot></div>
</template>
父组件使用:
TypeScript
<template>
<Slot1>111</Slot1> <!-- 替换插槽默认值为"111" -->
</template>
说明
- 父组件写入的内容会完全替换**
<slot>**内的默认值; - 若父组件未传内容,会显示插槽默认值。
2. 具名插槽
核心逻辑 :子组件给**<slot>** 加**name** 属性命名,父组件通过**<template #插槽名>**精准替换对应插槽。
子组件代码:
TypeScript
<script setup lang="ts"></script>
<template>
具名插槽
<div class="item head">
<slot name="head"></slot> <!-- 头部命名插槽 -->
</div>
<div class="item body">
<slot></slot> <!-- 默认插槽(等价于name="default") -->
</div>
<div class="item footer">
<slot name="footer"></slot> <!-- 底部命名插槽 -->
</div>
</template>
<style scoped>
.item{ width: 200px; height: 50px; }
.head{ background-color: #282828; }
.body{ background-color: #e5bdbd; }
.footer{ background-color: #796f6f; }
div{ color: white; }
</style>
父组件使用:
TypeScript
<template>
<Slot2>
123 <!-- 替换默认插槽(body区域) -->
<template #head>head</template> <!-- 替换head插槽 -->
<template #footer>footer</template> <!-- 替换footer插槽 -->
</Slot2>
</template>
说明
- #插槽名 是**
v-slot:插槽名**的简写,是 Vue3 推荐的简洁语法; - 未命名的**
<slot>** 默认对应**name="default"**,父组件直接写内容即可替换。
3. 作用域插槽
核心逻辑 :子组件向插槽传递数据(通过**v-bind**绑定),父组件接收后自定义渲染逻辑,解决 "子组件数据、父组件决定渲染样式 / 结构" 的场景。
子组件代码:
TypeScript
<script setup lang="ts">
import { ref } from 'vue';
// 子组件内部数据
const list = ref([
{"name":"Liu",age:20},
{"name":"He",age:18}
])
</script>
<template>
作用域插槽
<div class="ul">
<div class="item" v-for="item in list">
<!-- 向插槽传递数据:v-bind:data = 子组件数据 -->
<slot :data="item">
<!-- 插槽默认渲染逻辑(父组件未自定义时生效) -->
<span class="name">name:{{ item.name }}</span>
<span>age:{{ item.age }}</span>
</slot>
</div>
</div>
</template>
<style scoped>
/* 仅作用于插槽默认渲染内容 */
.name{ color: red; }
</style>
父组件使用
TypeScript
<template>
<Slot3>
<!-- 接收子组件传递的插槽数据:#default="自定义对象名" -->
<template #default="slotdata">
<div>
<i class="name">{{ slotdata.data.name }}</i>
</div>
</template>
</Slot3>
</template>
<style scoped>
/* 父组件自定义样式覆盖默认样式 */
.name{ color: aqua; }
</style>
说明
- 子组件传值:通过**
v-bind:xxx="数据"**向插槽暴露数据(可绑定多个属性); - 父组件接收:
#default="自定义对象名",对象内包含子组件传递的所有数据; - 样式规则:子组件**
scoped**样式仅作用于插槽默认内容,父组件自定义内容的样式由父组件控制。
三、小结
- 核心价值:让组件结构更灵活,父组件可按需定制子组件的部分内容,无需重复封装子组件;
- 注意点:Vue3 中插槽语法更简洁(
#简写),结合 TypeScript 可提升代码类型安全性。 - 选型技巧:
- 单区域内容替换 → 默认插槽;
- 多区域内容替换 → 具名插槽;
- 子数据父渲染 → 作用域插槽;
看完赶紧去敲一敲,动动手才是真理