EventBus是什么?
-
event值得是事件 bus 指的是公共汽车
用公共汽车的固定的站点, 处理上下车的事件, 来比做订阅与事件的分发
-
在编程中的eventbus通常是用来, 一发布 订阅的方式来传递数据的.
预先订阅事件, 当事件触发时可以接收大事件相关的数据
-
如何实现
需要用到的基础知识
原型对象与原型链, 回调函数
基础实现思路:
-
准备一个类或构造函数, 用来存放, 事件和订阅者信息
-
在该类/构造函数的原型上添加发布和订阅的方法
-
发布的方法需要传入, 要发布的事件名称, 事件的数据作为参数
-
订阅方法需要传入, 订阅的事件名称, 事件的处理函数(回调函数)
-
-
主要流程, 通过构造函数的实例, 调用其原型链上的 订阅方法, 传入订阅的事件和方法, 存储区该实例对应属性中(这里用Map)--消息发布者, 通过传入的事件名称, 在实例的数据中取出订阅这先前传入的订阅函数, 并调用, 同时将发布的信息内容, 传入即可
实现方法: 这里我们用构造函数的语法糖 class
class _EventBus { constructor() { //当实例化_EventBus时, 构造函数内的实例属性 this._events = new Map() //这里使用 map 的 k-v分别储存 事件名-对应的订阅者信息 //constructor中都有 prototype属性, 该属性指向构造函数, //所以prototype上的方法可以访问实例上的属性 } }
下面我们在原型对象上添加发布者方法
// 发布 //type为发布的信息名称 //...args为发布的信息内容, 表示可以接收多个, 最终会以数组形式存入args _EventBus.prototype.myPub = function (type, ...args) { //根据发布的事件名称获取对应的订阅者(们,这里我们为了可以有多个订阅者采用数组形式) let subEvents = this._events.get(type) //如果有, 那么遍历所有订阅者信息, 调用订阅者的方法发布信息 //注意: 这里会给所有的同一信息订阅者发布信息, //如果需要定制化, 可以订阅者打上唯一标识, 并作为发布的检索条件(就是再嵌套一层Map) if (subEvents) { //这里使用len以便减少动态的获取数组长度 for (let i = 0, len = subEvents.length; i < len; i++) { if (args.length > 2) { //如果传入参数超过两个, 那么我们使用apply subEvents[i].apply(this, args) //使用apply直接传入数组 } else { subEvents[i].call(this, ...args) //使用call原地打散 //(注意: 如果数组存有深层次的对象, 这里只能实现浅拷贝) } } } else { //如果没有该事件, 添加一个, 订阅者为null this._events.set(type, null) } }
下面在原型上添加订阅的方法
// 订阅 //type为发布的信息名称 //subEvent为订阅时, 存储再实例属性中的处理函数(回调函数, 会在发布时执行该回调) _EventBus.prototype.mySub = function (type, subEvent) { //获取type类型的处理, 如果没有, 则添加订阅 const subEvents = this._events.get(type) if (!subEvents) { //如果type事件没有对应的发布方法, 那么就把订阅的方法传给发布者 //这里采用数组的形式, 如果有多个订阅者 this._events.set(type, [subEvent]) } else { subEvents.push(subEvent) // 如果已有订阅者则, 直接追加 } return subEvent //将该方法返回最用会用来移除指定的订阅者 }
下面可以简单的使用了
// 实例化 const _eb = new _EventBus() // 订阅 //参数的个数和发布的一致哦(少了接收的介绍, 多出来显示 undefined) _eb.mySub('eventName', (n1, n2, n3) => { console.log(n1, n2, n3) }) _eb.mySub('evebtAge', age => { console.log(`接收到的年龄是 ${age}`) }) // 发布消息 _eb.myPub('eventName', 'Tom', 'Jerry', 'Tang') //Tom Jerry Tang _eb.myPub('evebtAge', 18) //接收到的年龄是 18
下面我们尝试实现删除订阅事件
//移除订阅 //subEvent是调用订阅函数 mySub 时返回的, 传入函数本体, 我们要把它揪出来删掉 _EventBus.prototype.removeSub = function (type, subEvent) { //获取对应消息的所有订阅者信息 const subEvents = this._events.get(type) // 遍历, 找出对应的subEvent 删除 for (let i = 0, len = subEvents.length; i < len; i++) { if (subEvents[i] === subEvent) { //如果mySub返回值使用定义的变量直接接收, 这里是全等 subEvents.splice(i, 1) //从i开始, 删除1个 return true //结束 } } }
以上就是我大概理解的事件发布-订阅机制, 如有差误之处, 还望给大佬指点
附上一个我的麻瓜例子, 可以直接使用, 一看就明白的那种在Vue中的使用
-
参考文章: