JS高级 - 事件总线

362 阅读2分钟

事件总线主要被用于在跨文件或跨组件之间进行数据的通信

常见有很多第三方库可以被用来实现对应的事件总线,例如mittevents

事件总线本质上就是一个挂载了对应事件响应函数的全局对象,事件总线并不知道也不需要知道是那个文件或组件触发了对应的事件回调函数或是那个文件或组件监听了对应的事件回调函数

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

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

事件总线中常见API

API说明
on对某个事件进行监听
emit触发某个事件对应的事件函数
off取消对于某个事件的某个监听
clear清除事件总线上所有绑定的事件监听函数

简单实现

class EventBus {
  events = {}

  on(name, callback, thisArg) {
    if (typeof callback !== 'function') {
      throw new TypeError('callback is not a function')
    }

    if (!this.events[name]) {
      this.events[name] = []
    }

    this.events[name].push({
      fn: callback,
      thisArg
    })
  }

  emit(name, ...args) {
    const events = this.events[name]

    if (!events.length) {
      throw new Error('没有对应的事件响应函数')
    }

    for (const { fn, thisArg } of events) {
      if (thisArg) {
        fn.apply(thisArg, args)
      } else {
        fn(...args)
      }
    }
  }

  off(name, callback) {
    const events = this.events[name] ?? []

    const index = events.findIndex(event => event.fn === callback)

    if (index !== -1) {
      this.events[name].splice(index, 1)
    }

    if (!this.events[name].length) {
      delete this.events[name]
    }
  }

  clear() {
    this.events = {}
  }
}

const eventBus = new EventBus()

const fn = (name, age) => console.log(name, age)

eventBus.on('customEvent', (name, age) => console.log(name, age))
eventBus.on('customEvent', fn)

eventBus.emit('customEvent', 'Klaus', 23)

// 清除一个事件响应函数的时候
// 需要清楚的那个事件响应函数需要和绑定时候传入的那个事件响应函数一致
// 为同一个事件处理函数
eventBus.off('customEvent', fn)

eventBus.emit('customEvent', 'Klaus', 23)

console.log('------------------')

eventBus.on('fooEvent', (name, age) => console.log(name, age))
eventBus.on('fooEvent', function(name, age) { console.log(this, name, age)}, {name: 'Klaus'})

eventBus.emit('fooEvent', 'Klaus', 23)