手写实现简单的Vue事件总线

一、什么是事件总线

自定义事件总线属于一种观察者模式,其中包括三个角色:

  • 发布者(Publisher):发出事件(Event);
  • 订阅者(Subscriber):订阅事件(Event),并且会进行响应(Handler);
  • 事件总线(EventBus):无论是发布者还是订阅者都是通过事件总线作为中台的;

当然我们可以选择一些第三方的库:

  • Vue2默认是带有事件总线的功能;
  • Vue3中推荐一些第三方库,比如mitt;

二、手写实现事件总线

当然我们也可以实现自己的事件总线:

  • 事件的监听方法on:存储对应事件名需要执行的事件函数
  • 事件的发射方法emit:执行对应事件名需要执行的事件函数
  • 事件的取消监听off:删除对应事件名需要执行的事件函数
    手写实现简单的Vue事件总线_第1张图片
    运行结果:
    在这里插入图片描述
// 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]
        // 遍历newHandlers
        for (let i = 0; i < newHandlers.length; i++) {
            // 获取newHandlers中的每个handler
            const 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)

你可能感兴趣的:(JS高级,Vue.js,vue.js,javascript,前端,事件总线)