Event Bus

148 阅读1分钟

事件总线

事件总线 Event bus 通常作为多模块间的通信机制,如:Vue中可以通过 Event bus 在多个组件中传递数据。

Event Bus本质是采用发布订阅模式

通常包含一下几个功能

  • on
    • 订阅事件,传入事件名和事件的回调
  • emit
    • 发布事件,传入事件名即可
  • off
    • 取消事件,只传入事件名,默认取消所有事件,如果传入第二个参数:需要取消的事件,则只取消传入的事件
  • once
    • 只订阅一次事件

使用 Map 收集事件

class EventBus {
  constructor() {
    this.events = new Map()
  }

  /**
   * 订阅事件
   * @param {string} eventName
   * @param {Function} cb
   */
  on(eventName, cb) {
    if (typeof cb !== 'function') {
      throw new Error('cb is not a function')
    }
    if (!this.events.has(eventName)) {
      this.events.set(eventName, [])
    }
    const cbs = this.events.get(eventName)
    cbs.push({
      handle: cb
    })
  }

  /**
   * 发布事件
   * @param {string} eventName
   */
  emit(eventName) {
    if (this.events.has(eventName)) {
      const cbs = this.events.get(eventName)
      cbs.forEach((cb) => {
        cb.handle(...Array.prototype.slice.call(arguments, 1))
        if (cb.once) {
          // 取消订阅
          this.off(eventName, cb.handle)
        }
      })
    }
  }

  /**
   * 取消事件
   * @param {string} eventName
   * @param {Function|undefined} cb
   * @returns
   */
  off(eventName, cb) {
    if (!this.events.has(eventName)) {
      return
    }
    if (cb === undefined) {
      // 清除所有
      this.events.delete(eventName)
    } else {
      const cbs = this.events.get(eventName)
      const res = cbs.filter((item) => {
        return item.handle !== cb
      })
      this.events.set(eventName, res)
    }
  }

  /**
   * 订阅一次
   * @param {string} eventName
   * @param {Function} cb
   */
  once(eventName, cb) {
    if (!this.events.has(eventName)) {
      this.events.set(eventName, [])
    }
    const cbs = this.events.get(eventName)
    cbs.push({
      handle: cb,
      once: true
    })
  }
}