Vue3组件通信详解

Vue 3 中的组件通讯是 Vue 应用开发中非常重要的一环,它允许组件之间传递数据和方法,从而实现数据的共享和功能的调用。下面将分别介绍父子组件、孙子组件(祖孙组件)、兄弟组件之间的通讯方式,并给出示例代码和总结表格。

一、父子组件通讯

1. 父传子(props)

父组件通过 props 向子组件传递数据。

父组件示例代码

<template>  
  <ChildComponent :message="parentMessage" />  
</template>  
  
<script setup>  
import ChildComponent from './ChildComponent.vue';  
import { ref } from 'vue';  
  
const parentMessage = ref('Hello from Parent');  
</script>

子组件示例代码

<template>  
  <div>{{ message }}</div>  
</template>  
  
<script setup>  
import { defineProps } from 'vue';  
  
const props = defineProps({  
  message: String  
});  
</script>

2. 子传父($emit)

子组件通过 $emit 触发事件,向父组件传递数据或信号。

子组件示例代码

<template>  
  <button @click="sendMessage">Send Message to Parent</button>  
</template>  
  
<script setup>  
//this.$emit->”定义emit“,传数据
const emit = defineEmits(['update:message']);  
  
function sendMessage() {  
  emit('update:message', 'Hello from Child');  
}  
</script>

父组件示例代码(监听子组件触发的事件):

<template>  
  <ChildComponent @update:message="handleMessage" />  
</template>  
  
<script setup>  
import ChildComponent from './ChildComponent.vue';  
  
function handleMessage(message) {  
  console.log(message); // 输出: Hello from Child  
}  
</script>

二、祖孙组件通讯(provide/inject)

祖先组件通过 provide 选项提供数据或方法,后代组件通过 inject 选项接收数据或方法。

祖先组件示例代码

<template>  
  <ChildComponent />  
</template>  
  
<script setup>  
import { provide, ref } from 'vue';  
  
const grandParentData = ref('Data from GrandParent');  
  
provide('grandParentData', grandParentData);  
</script>

三、兄弟组件通讯

兄弟组件之间的通讯通常不直接进行,而是通过共同的父组件或全局状态管理(如 Vuex、Pinia)来实现。

示例:通过事件总线(不推荐,Vue 3 中可用第三方库如 mitt 替代)

事件总线(mitt.js)

// eventBus.js  
import mitt from 'mitt';  
const emitter = mitt();  
export default emitter;

组件A(发送方)

<template>  
  <button @click="sendMessage">Send Message to Brother</button>  
</template>  
  
<script setup>  
import emitter from './eventBus.js';  
  
function sendMessage() {  
  emitter.emit('message', 'Hello from Component A');  
}  
</script>

组件B(接收方)

<template>  
  <p>{{ message }}</p>  
</template>  
  
<script setup>  
import { ref } from 'vue';  
import emitter from './eventBus.js';  
  
const message = ref('');  
 //emitter.on接收,‘message’通信主体标识
emitter.on('message', (newMessage) => {  
  message.value = newMessage;  
});  
</script>

四、总结

通讯类型通讯方式发送方接收方示例说明
父子组件props父组件子组件父组件通过 props 传递数据给子组件
父子组件$emit子组件父组件子组件通过 $emit 触发事件,父组件监听并处理
祖孙组件provide/inject祖先组件后代组件祖先组件通过 provide 提供数据,后代组件通过 inject 接收
兄弟组件通过父组件兄弟组件A兄弟组件B兄弟组件A通过事件通知父组件,父组件再传递给兄弟组件B
兄弟组件全局状态管理(Vuex/Pinia)兄弟组件A兄弟组件B使用 Vuex 或 Pinia 管理全局状态,兄弟组件共享状态
兄弟组件第三方库(如 mitt)兄弟组件A兄弟组件B通过第三方库实现事件总线,兄弟组件间通过事件总线通讯

 五、事件总线底层原理

设计模式—观察者模式与发布订阅-CSDN博客

Javascript常见设计模式-CSDN博客

发布——订阅者模式

发布-订阅模式是一种消息传递模式,允许发送者(发布者)发送消息而不直接关心谁将接收这些消息,同时接收者(订阅者)可以监听并接收这些消息,而不需要了解消息是从哪里发送的。mitt 作为一个轻量级的事件总线库,正是基于这种模式设计的。

在 mitt 中,你可以:

  • 使用 .on(eventName, handler) 方法来订阅一个事件,其中 eventName 是事件的名称(一个字符串),而 handler 是当事件发生时被调用的处理函数。
  • 使用 .emit(eventName, ...args) 方法来发布一个事件,其中 eventName 是事件的名称,而 ...args 是传递给事件处理函数的参数列表。
  • (可选地)使用 .off(eventName, handler) 方法来取消订阅之前订阅的事件。如果不提供 handler,则取消该事件的所有订阅者。

这些操作完全符合发布-订阅模式的典型特征:

  • 发布者(在 mitt 的上下文中是调用 .emit() 方法的代码)发布一个事件,不需要知道有哪些订阅者会接收这个事件。
  • 订阅者(在 mitt 的上下文中是通过 .on() 方法注册的处理函数)监听并响应特定的事件,不需要知道事件是由谁发布的。
  • 事件总线(在 mitt 的上下文中是 mitt 实例本身)负责事件的分发,确保事件能够到达所有相关的订阅者。

因此,可以说 mitt 确实是通过发布-订阅模式来实现其功能的。

// 创建一个简单的发布订阅者管理类  
class EventEmitter {  
    constructor() {  
        this.events = {}; // 用于存储事件的字典,键为事件名,值为订阅者数组  
    }  
  
    // 订阅事件  
    on(eventName, callback) {  
        if (!this.events[eventName]) {  
            this.events[eventName] = []; // 如果该事件尚未被订阅,则初始化一个空数组  
        }  
        this.events[eventName].push(callback); // 将回调函数添加到订阅者数组中  
        return this; // 支持链式调用  
    }  
  
    // 取消订阅事件  
    off(eventName, callback) {  
        if (this.events[eventName]) {  
            // 移除指定的回调函数  
            this.events[eventName] = this.events[eventName].filter(cb => cb !== callback);  
            // 如果订阅者数组为空,则删除该事件  
            if (this.events[eventName].length === 0) {  
                delete this.events[eventName];  
            }  
        }  
        return this; // 支持链式调用  
    }  
  
    // 发布事件  
    emit(eventName, ...args) {  
        if (this.events[eventName]) {  
            // 遍历订阅者数组,并执行每个回调函数  
            this.events[eventName].forEach(callback => {  
                callback.apply(this, args);  
            });  
        }  
        return this; // 支持链式调用  
    }  
  
    // 可选:监听一次后自动取消订阅  
    once(eventName, callback) {  
        const onceCallback = (...args) => {  
            callback.apply(this, args);  
            this.off(eventName, onceCallback);  
        };  
        this.on(eventName, onceCallback);  
        return this; // 支持链式调用  
    }  
}  
  
// 使用示例  
const eventBus = new EventEmitter();  
  
// 订阅者1  
function subscriber1(price) {  
    console.log('订阅者1收到消息:当前价格已降至' + price + '元');  
}  
  
// 订阅者2  
function subscriber2(price) {  
    console.log('订阅者2也收到消息:价格更新为' + price + '元');  
}  
  
// 订阅事件  
eventBus.on('priceUpdate', subscriber1);  
eventBus.on('priceUpdate', subscriber2);  
  
// 使用once方法监听一次  
eventBus.once('specialOffer', (offer) => {  
    console.log('只接收一次的特别优惠:' + offer);  
});  
  
// 发布事件  
eventBus.emit('priceUpdate', 99); // 订阅者1和订阅者2都会收到消息  
eventBus.emit('specialOffer', '买一赠一'); // 只有一个订阅者会收到这个特别优惠的消息  
  
// 取消订阅  
eventBus.off('priceUpdate', subscriber1);  
eventBus.emit('priceUpdate', 88); // 只有订阅者2会收到消息

  • 11
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值