当前位置: 首页 > news >正文

Vue基础知识-脚手架开发-任意组件通信-事件总线($bus)与消息订阅发布(pubsub-js)

一、项目概况与结构

本案例通过「School 组件」和「Student 组件」(兄弟关系,共同父组件是 App)演示通信:

  • 功能 1:School 组件点击按钮,通过「事件总线」传递消息给 Student 组件;
  • 功能 2:School 组件点击按钮,通过「pubsub-js」传递消息给 Student 组件;
  • 核心:两种方案均实现 “School 发消息,Student 收消息” 的兄弟通信逻辑。

项目结构:

src/
├─ components/       # 兄弟组件目录
│  ├─ School.vue     # 消息发送方
│  └─ Student.vue    # 消息接收方
├─ App.vue           # 父组件(仅作为容器,不参与通信)
└─ main.js           # 入口文件(事件总线初始化关键)

二、完整源码实现

1. 入口文件:main.js(事件总线初始化)

事件总线($bus)的核心是在 Vue 原型上挂载一个全新的 Vue 实例,作为“事件载体”(所有组件都能通过原型链访问到这个实例)。


import Vue from 'vue'import App from './App.vue'Vue.config.productionTip = false/*   /*   前置知识:1 $on,$emit来自Vue.prototype2 VueComponent.prototype.prototype === Vue.prototype 
*/ 
Vue.prototype.$bus = new Vue()/*   此时,Vue原型上的$bus是全新的vm。又因为VueComponent.prototype.prototype === Vue.prototype。因此任何vc都可以使用this.$bus.$on()往$bus(沿着原型链在全新的vm)上绑定自定义事件并给回调函数同时,任何vc可以使用this.$bus.$emit来触发$bus上的自定义事件。实现任意组件间的通信注:一个事件可以对应多个回调函数。当事件发生时,所有注册了该事件的回调函数都会被依次调用。但一般需一一对应
*/ 
new Vue({render: h => h(App),beforeCreate(){}
}).$mount('#app')

2. 兄弟组件 1:School.vue(消息发送方)

负责通过两种方案发送消息:

  • 事件总线:通过this.$bus.$emit(事件名, 数据)触发事件;
  • pubsub-js:通过pubsub.publish(消息名, 数据)发布消息。
<template><div class="demo"><h2>学校名称:{{name}}</h2><input type="button" value="兄弟相传School-Studnet(事件总线实现)" @click="setMsg"/><!--消息订阅与发布:npm install pubsub-js--><input type="button" value="兄弟相传School-Studnet(消息订阅/发布实现)" @click="setMsg2"/></div>
</template>
<script>//导入import pubsub from 'pubsub-js'export default {name:'School',data(){return {name:'北京大学',msg:'你好呀,$bus',msg2:'你好呀,pubsub-js'}},methods:{setMsg(){this.$bus.$emit('getMsg',this.msg)},setMsg2(){pubsub.publish('getMsg2',this.msg2)},},}
</script><style scoped>.demo{background-color: red;padding:10px;}
</style>

3. 兄弟组件 2:Student.vue(消息接收方)

负责通过两种方案接收消息,并在组件销毁前 “解绑事件 / 取消订阅”(防止内存泄漏):

  • 事件总线:通过this.$bus.$on(事件名, 回调)绑定事件;
  • pubsub-js:通过pubsub.subscribe(消息名, 回调)订阅消息。
<template><div class="demo"><h2>学生名称:{{name}}</h2><h2>msg:{{msg}}</h2><h2>msg2:{{msg2}}</h2></div>
</template>
<script>//导入import pubsub from 'pubsub-js'export default { name:'Student',data(){return {name:'张三',msg:'',msg2:''}},methods:{getMsg(msg){this.msg = msg},//注意第一个参数是消息名。getMsg2(msgName,msg2){this.msg2 = msg2}},mounted(){this.$bus.$on('getMsg',this.getMsg)this.pubId = pubsub.subscribe('getMsg2',this.getMsg2)//写成匿名函要求使用箭头函数},beforeDestroy(){//记得解绑事件,因为是公共的vue实例对象。自定义事件在组件实例对象。组件实例对象销毁则自定义事件自然销毁this.$bus.$off('getMsg')//解绑getMsg事件下所有回调;//this.$bus.$off('getMsg',this.getMsg)仅解绑该事件名下与 “回调函数” 匹配的回调//取消订阅pubsub.unsubscribe(pubId)}}
</script><style scoped>.demo{background-color: orange;padding: 10px;margin-top: 30px;}
</style>

4. 父组件:App.vue(仅作为容器)

App 组件仅负责渲染两个兄弟组件,不参与通信逻辑(体现兄弟组件 “直接通信” 的特点)。

<template><div class="app"><School /><Student /></div>
</template><script>import School from './components/School'  import Student from './components/Student';   export default {name:'App',data() {return {schoolName:'',studentlName:''}},components:{School,Student,},}
</script><style>.app{background-color: gray;padding: 10px;}</style>

三、核心原理解析

1. 方案 1:事件总线($bus)

(1)为什么能实现兄弟通信?

事件总线的核心是全局共享的 Vue 实例this.$bus),它具备以下特性:

  • Vue 实例自带$on(绑定事件)、$emit(触发事件)、$off(解绑事件)方法,可作为 “事件容器”;
  • 通过Vue.prototype.$bus = new Vue(),所有组件实例(vc)都能通过原型链访问到这个$bus(相当于 “全局变量”);
  • 发送方通过$bus.$emit触发事件,接收方通过$bus.$on绑定事件,$bus作为中间载体传递消息。
(2)关键步骤(3 步)
  1. 初始化:Vue.prototype.$bus = new Vue();
  2. 发送消息:发送方用this.$bus.$emit(事件名, 数据)触发事件;
  3. 接收消息:接收方用this.$bus.$on(事件名, 回调)绑定事件,回调中处理数据;
  4. 解绑事件:接收方在beforeDestroy中用this.$bus.$off解绑,防止内存泄漏。

2. 方案 2:消息订阅发布(pubsub-js)

(1)什么是 pubsub-js?

pubsub-js 是一个独立的 JavaScript 库,基于 “发布 - 订阅模式”(Publish/Subscribe)实现跨组件通信,核心思想是:

  • 发布者(Publisher):发送消息(如 School 组件用pubsub.publish);
  • 订阅者(Subscriber):接收消息(如 Student 组件用pubsub.subscribe);
  • 消息中心:pubsub 库自身作为 “中间件”,管理所有消息的发布与订阅。
(2)关键步骤(4 步)
  1. 安装依赖:项目根目录执行npm install pubsub-js --save
  2. 导入库:在发送方和接收方组件中import pubsub from 'pubsub-js'
  3. 发布消息:发送方用pubsub.publish(消息名, 数据)发布消息;
  4. 订阅消息:接收方用pubsub.subscribe(消息名, 回调)订阅消息(回调第一个参数是消息名,第二个是数据);
  5. 取消订阅:接收方在beforeDestroy中用pubsub.unsubscribe(订阅ID)取消,防止内存泄漏。

四、两种方案对比与适用场景

对比维度事件总线($bus)消息订阅发布(pubsub-js)
依赖无(基于 Vue 原生 API,无需额外安装)需安装第三方库(pubsub-js)
核心载体全局 Vue 实例(this.$buspubsub 库自身(消息中心)
语法简洁度高($emit/$on/$off,Vue 开发者熟悉)中(publish/subscribe/unsubscribe
回调参数直接接收数据(如handle(data)第一个参数是消息名(如handle(name, data)
多消息管理需手动区分事件名,易冲突消息名独立管理,冲突风险低
跨框架兼容性仅 Vue(依赖 Vue 实例方法)通用(支持 Vue/React/ 原生 JS 等)
内存泄漏风险高(需手动$off解绑,否则事件残留)中(需保存订阅 ID,用unsubscribe取消)
适用场景中小型 Vue 项目,兄弟 / 跨级组件简单通信大型项目,多组件复杂通信(如跨模块)

五、注意事项

1. 事件总线($bus)

  • 事件名冲突:不同组件若用相同事件名(如'sendMsg'),可能导致回调函数重复触发,建议用 “组件名 - 方向 - 事件名” 命名(如'school-to-student-bus');
  • 必须解绑事件$bus是全局实例,组件销毁后若不$off解绑,事件会残留(下次创建组件时会重复绑定,导致回调多次执行);
  • 避免绑定匿名函数:若用this.$bus.$on('事件名', (data) => { ... }),解绑时无法定位到具体回调(需用命名函数,如this.handleBusMsg)。

2. pubsub-js 

  • 回调参数顺序:订阅回调的第一个参数是 “消息名”,第二个才是 “数据”(新手易忽略,导致data拿到消息名);
  • 保存订阅 IDpubsub.subscribe会返回一个唯一 ID(如12),必须用this.pubsubId保存,否则无法精准取消订阅(pubsub.unsubscribe需传入该 ID);
  • 取消订阅时机:必须在beforeDestroy中取消订阅(组件销毁后若不取消,消息触发时会继续执行回调,导致内存泄漏)。

六、总结

  • 事件总线($bus):Vue 原生方案,轻量简洁,适合中小型 Vue 项目的兄弟 / 跨级通信,核心是 “全局 Vue 实例作为事件载体”,需注意事件名冲突和手动解绑;
  • pubsub-js:第三方通用方案,支持跨框架,适合大型项目的复杂通信,核心是 “发布 - 订阅模式”,需注意回调参数顺序和订阅 ID 管理。

文章转载自:

http://ZpKo3993.mdnnz.cn
http://IleGaCIE.mdnnz.cn
http://krUGfXuT.mdnnz.cn
http://uAMJuGvX.mdnnz.cn
http://hue1yvnu.mdnnz.cn
http://4B17vlYd.mdnnz.cn
http://IlNpBWc8.mdnnz.cn
http://b21of8w6.mdnnz.cn
http://iHORJ9mh.mdnnz.cn
http://da88G8xJ.mdnnz.cn
http://iXBhKM9c.mdnnz.cn
http://MWXvP4wj.mdnnz.cn
http://Hcu6xQXR.mdnnz.cn
http://pvxFOA3o.mdnnz.cn
http://OHd5squ6.mdnnz.cn
http://SEN4yAtH.mdnnz.cn
http://tEiws0o9.mdnnz.cn
http://wFxp31zN.mdnnz.cn
http://iB9Ha3Zb.mdnnz.cn
http://nti6e1Z2.mdnnz.cn
http://6bknKVK2.mdnnz.cn
http://388l17Lq.mdnnz.cn
http://jTBbAzOS.mdnnz.cn
http://9jdgDCt8.mdnnz.cn
http://YzFo04xE.mdnnz.cn
http://LeG6jbVh.mdnnz.cn
http://TluxoRhF.mdnnz.cn
http://0K197ZE7.mdnnz.cn
http://0g2GRQN4.mdnnz.cn
http://w6QmS0h3.mdnnz.cn
http://www.dtcms.com/a/369413.html

相关文章:

  • python中等难度面试题(1)
  • 关于SFP(Small Form-factor Pluggable)模块的全面解析,从技术规格到市场应用的系统化说明:
  • LeetCode Hot 100 第11天
  • daily notes[10]
  • JAiRouter 0.7.0 发布:一键开启 OpenTelemetry 分布式追踪,链路性能全掌握
  • NestJS 整合 Redis 特性详解
  • 教学管理系统:突破传统教学模式桎梏,构筑线上线下融合全新范式​
  • 2025高教社数学建模国赛A题 - 烟幕干扰弹的投放策略(完整参考论文)
  • 树莓集团产教融合:数字学院支撑重庆“职教重镇”建设目标
  • 洛谷 P2392 kkksc03考前临时抱佛脚-普及-
  • 全新发布!CodeBuddy 插件升级 3.3,立即更新获取新功能!
  • 不改代码,不重启,我把线上线程池的核心数从 10 改成了 100
  • 红黑树 + 双链表最小调度器原型
  • MySQL InnoDB 的 MVCC 机制
  • CRYPT32!CryptMsgUpdate函数分析两次CRYPT32!PkiAsn1Decode的作用
  • 智能健康新纪元:第一视角计算如何重塑科学减肥认知
  • Linux常见命令总结 合集二:基本命令、目录操作命令、文件操作命令、压缩文件操作、查找命令、权限命令、其他命令
  • FairGuard游戏加固产品常见问题解答
  • 2025年外贸服装软件TOP3推荐榜单,高效管理必备选择
  • 为什么说 Linode 和 DigitalOcean 的差距,不止于 VPS?
  • 十大常用算法(待更新)
  • c#动态树形表达式详解
  • 字符串格式化——`vsnprintf`函数
  • 【Flutter】drag_select_grid_view: ^0.6.2 使用
  • Android的DTBO详解
  • C++小数精度、四舍五入的疑惑
  • 操作系统——同步与互斥
  • 2025年跨领域管理能力提升认证路径分析
  • 常用的轻代码软件哪个好?
  • 双轴倾角传感器厂家与物联网角度传感器应用全解析