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会收到消息
相关推荐
web1478621072311 分钟前
C# .Net Web 路由相关配置
前端·c#·.net
m0_7482478012 分钟前
Flutter Intl包使用指南:实现国际化和本地化
前端·javascript·flutter
飞的肖16 分钟前
前端使用 Element Plus架构vue3.0实现图片拖拉拽,后等比压缩,上传到Spring Boot后端
前端·spring boot·架构
青灯文案123 分钟前
前端 HTTP 请求由 Nginx 反向代理和 API 网关到后端服务的流程
前端·nginx·http
m0_7482548828 分钟前
DataX3.0+DataX-Web部署分布式可视化ETL系统
前端·分布式·etl
ZJ_.39 分钟前
WPSJS:让 WPS 办公与 JavaScript 完美联动
开发语言·前端·javascript·vscode·ecmascript·wps
GIS开发特训营44 分钟前
Vue零基础教程|从前端框架到GIS开发系列课程(七)响应式系统介绍
前端·vue.js·前端框架·gis开发·webgis·三维gis
Cachel wood1 小时前
python round四舍五入和decimal库精确四舍五入
java·linux·前端·数据库·vue.js·python·前端框架
学代码的小前端1 小时前
0基础学前端-----CSS DAY9
前端·css
joan_851 小时前
layui表格templet图片渲染--模板字符串和字符串拼接
前端·javascript·layui