1. EventEmitter 发布订阅模式

Vue里面用过eventBus作为通信,方式,实现方式是用Vue函数的$on/$emit方法。

类似 node 中的 events 模块下面的 EventEmitter 类

这种方式可以叫做发布订阅模式

class MyEventEmitter {
  constructor() {
    this.events = {}
  }

  on (event, cbFn) {
    if (!this.events[event]) {
      this.events[event] = []
    }
    this.events[event].push(cbFn)
    return this
  }

  off (event, cbFn) {
    if (!cbFn) {
      this.events[event] = []
      return this
    }
    if (this.events[event]) {
      this.events[event] = this.events[event].filter(item => item !== cbFn)
    }
    return this
  }

  once (event, cbFn) {
    const onceFn = () => {
      cbFn.apply(this, arguments)
      this.off(event, onceFn)
    }
    this.on(event, onceFn)
    return this
  }

  emit (event, ...args) {
    if (this.events[event]) {
      this.events[event].forEach(item => item.call(this, ...args))
    }
  }
}

// test
const myEvent = new MyEventEmitter()

myEvent.on('test1', () => {
  console.log('test-11')
}).once('test2', () => {
  console.log('test-22')
})

myEvent.emit('test1')
myEvent.emit('test2')
myEvent.emit('test2')
class EventBus {
  constructor () {
    this.events = new Map()
  }

  on(event, cbFn) {
    if (!this.events.has(event)) this.events.set(event, new Set())
    this.events.get(event).add(cbFn)
    return this
  }

  off(event, cbFn) {
    if (!cbFn) {
      this.events.delete(event)
      return this
    }
    if (this.events.has(event)) {
      this.events.get(event).delete(cbFn)
    }
    return this
  }

  once(event, cbFn) {
    const onceFn = () => {
      cbFn.call(this)
      this.off(event, onceFn)
    }
    this.on(event, onceFn)
    return this
  }

  emit(event, ...args) {
    if (this.events.has(event)) {
      this.events.get(event).forEach(item => {
        item.apply(this, args)
      });
    }
  }
}

const bus = new EventBus()
const fn = (...args) => console.log('t1', ...args)
const fn2 = (...args) => console.log('t2', ...args)
bus.on('t1', fn).once('t2', fn2)
bus.off('t1', fn)
bus.emit('t1', 1, 2)
bus.emit('t2')
bus.emit('t2')

1.1. 细节注意点

  • 返回 this 是为了链式调用

  • on 方法需要考虑首次添加事件 this.events[event] 不存在,直接复制空数组

  • off 方法中的 filter 方法不会改变原数组的值,所以需要重新赋值:this.events[event] = this.events[event].filter(item => item !== cbFn)

  • off 方法分两种情况:1. this.off(event), 直接清空数组; 2. this.off(event, cbFn), 只删除对应的事件

  • once 方法的实现思路:对 cbFn 事件进行包裹,内部执行 cbFn 函数后,立即接触事件绑定(off 方法),然后对包裹后函数进行事件绑定,即调用 on 方法

  • emit 方法要考虑两种特殊情况:

    • event 不存在,从未绑定过,所以需要先判断 this.events[event] 是否存在,否者报错。

    • 调用 emit 的时候,也可能要传递参数,比如:我们调用 on 方法的时候,cbFn 是带参数的。

Copyright © tomgou 2022 all right reserved,powered by Gitbook该文章修订时间: 2023-09-01 15:27:44

results matching ""

    No results matching ""