一、什么是事件总线
自定义事件总线属于一种观察者模式,其中包括三个角色:
- 发布者(Publisher):发出事件(Event);
- 订阅者(Subscriber):订阅事件(Event),并且会进行响应(Handler);
- 事件总线(EventBus):无论是发布者还是订阅者都是通过事件总线作为中台的;
当然我们可以选择一些第三方的库:
- Vue2默认是带有事件总线的功能;
- Vue3中推荐一些第三方库,比如mitt;
二、手写实现事件总线
当然我们也可以实现自己的事件总线:
- 事件的监听方法on:存储对应事件名需要执行的事件函数
- 事件的发射方法emit:执行对应事件名需要执行的事件函数
- 事件的取消监听off:删除对应事件名需要执行的事件函数
运行结果:
// eventBus对象:
// {
// abc: [
// {需要监听的函数, 为需要监听的事件函数绑定的this},
// {需要监听的函数, 为需要监听的事件函数绑定的this}
// ]
// }
class EventBus {constructor() {this.eventBus = {}}/** on函数:* 被调用时,需要把eventCallback和thisArg放到一个对象中,然后把这个对象push到一个数组里,* 然后把eventName作为key,把这个数组作为value存到eventBus对象中* eventName:需要监听的事件名称* eventCallback:需要监听的事件函数* thisArg:为需要监听的事件函数绑定this*/on(eventName, eventCallback, thisArg) {let handlers = this.eventBus[eventName]if (!handlers) {// 如果在eventBus对象中找不到key为eventName的handlers,// 则创建一个handlers空数组,并放到eventBus对象中handlers = []this.eventBus[eventName] = handlers}// 如果handlers存在,则把需要监听的eventCallback函数、函数需要绑定的this// 以对象的形式存到handlers中handlers.push({eventCallback,thisArg})}/** emit函数:* 一旦被调用,则需要执行eventBus对象中key为eventName所对应的的数组中* 的每个对象中的eventCallback函数*/emit(eventName, ...payload) {// 获取eventBus对象中key为eventName所对应的的数组const handlers = this.eventBus[eventName]if (!handlers) return// 如果数组存在则遍历数组,调用需要执行的事件函数handlers.forEach(handler => {handler.eventCallback.apply(handler.thisArg, payload)})}/** off函数:* 被调用时,删除eventBus中key为eventName,* value为一个handler对象,且该对象中的eventCallback属性与off函数第二个参数相等的这个value对象*/off(eventName, eventCallback) {// 获取eventBus对象中key为eventName所对应的的数组const handlers = this.eventBus[eventName]if (!handlers) return// 复制handlers,然后使用newHandlers新数组来进行遍历,确保遍历的数组是始终保持不变的// 防止出现后续删除某个handlers数组中的对象后,在进行遍历时出现问题const newHandlers = [...handlers]// 遍历newHandlersfor (let i = 0; i < newHandlers.length; i++) {// 获取newHandlers中的每个handlerconst handler = newHandlers[i]// 如果handler的eventCallback 等于 参数中传进来的eventCallback,// 则获取到这个handler对象在handlers数组中的下标,然后删除这个handler对象if (handler.eventCallback === eventCallback) {const index = handlers.indexOf(handler)handlers.splice(index, 1)}}}
}// 以下为测试代码:
const eventBus = new EventBus()// main.js文件
eventBus.on('abc', function (payload) {console.log('监听abc事件', this, payload)
}, {name: 'zep'})const handleCallback = function (payload) {console.log('监听abc事件', this, payload)
}
eventBus.on('abc', handleCallback, {name: 'lala'})// utils.js文件
eventBus.emit('abc', 123)eventBus.off('abc', handleCallback)
eventBus.emit('abc', 123)