不到百行代码实现一个事件总线,你确定不看吗?

667 阅读3分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第5天,点击查看活动详情

什么是EventBus

EventBus 又称为事件总线。在Vue中可以使用 EventBus 来作为沟通桥梁的概念,就像是所有组件共用相同的事件中心,可以向该中心注册发送事件或接收事件,所以组件都可以上下平行地通知其他组件,但也就是太方便所以若使用不慎,就会造成难以维护的灾难,因此才需要更完善的Vuex作为状态管理中心,将通知的概念上升到共享状态层次。

在vue中我们有时候为了在关系很远的组件中传递数据,这时就会用到事件总线。我们只需要在需要接受数据的组件中监听定义的事件名称,然后在指定回调函数。在传数据的组件中发射对应事件名的事件,并携带数据,这时候回调函数就会被执行,然后就可以获取到指定的数据了。相对于vuex来说,事件总线更方便。

介绍了这么多,我们也知道了事件总线的三个功能,分别是

  • on, 监听事件

  • emit,发射事件

  • off, 移除事件

首先,我们要写一个工具类,我们得先把一些功能和变量定义好。

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

  emit(eventName, payload) {
 
  }

  on(eventName, callback) {
    
  }

  off(eventName, callback) {
    
  }
}

tips: 这里用到了map来存储事件名称和对应的回调函数,考虑到一个事件对应不止一个回调函数,则用数组来存储。

接着,我们来一个一个实现对应的功能。

实现on函数

参数接收: 事件名称,回调函数。

实现思路: 先从map对象中get当前事件名称,看是否已经有值。若没有值,则初始化为空数组,然后往数组里push回调函数;若有值,则判断是否已经存在。最后就是更新map的key为事件名称对应的值

  on(eventName, callback) {
    testArg(eventName, 'string')
    testArg(callback, 'function')
    // 获取事件名称对应的函数数组
    let fnArray = this.fns.get(eventName)
    // 第一次
    if (fnArray === null || fnArray === undefined) {
      fnArray = []
      fnArray.push(callback)
    } else {
      fnArray.indexOf(callback) === -1 ? fnArray.push(callback) : null
    }

    this.fns.set(eventName, fnArray)
  }

这里封装了一个参数校验的函数,实现如下

function testArg(args, type) {
  if (typeof args !== type) {
    throw Error(`the ${args} must be ${type}!`)
  }
}

实现emit函数

参数接收: 事件名称,参数。

实现思路: 从map中获取当前事件名称对应的函数数组,然后执行数组中的函数并传递参数。

 emit(eventName, payload) {
    testArg(eventName, 'string')

    let fnArray = this.fns.get(eventName)

    if (fnArray === undefined || fnArray === null || fnArray.length === 0) {
      console.log(`${eventName} 事件没有监听函数!!!`)
      return
    }

    fnArray.forEach((fn) => {
      fn.call(this, payload)
    })
  }

实现off函数

参数接收: 事件名称,回调函数。

实现思路: 从map中获取当前事件名称对应的函数数组,然后看是否存在要移除的函数,最后执行对应的操作。


 off(eventName, callback) {
    testArg(eventName, 'string')
    testArg(callback, 'function')

    let fnArray = this.fns.get(eventName)
    if (fnArray === undefined || fnArray === null || fnArray.length === 0) {
      console.log(`${eventName} 事件没有监听函数!!!`)
      return
    }
    const index = fnArray.indexOf(callback)
    if (index === -1) {
      console.log(`${eventName} 事件没有监听函数!!!`)
      return
    } else {
      fnArray.splice(index, 1)
    }

    this.fns.set(eventName, fnArray)
  }

好了,我们已经实现了三个功能,我们来写一段代码测试一下

const EventBus = require('./index')

function test1() {
  console.log('test1')
}

function test2() {
  console.log('test2')
}

EventBus.on('test', test1)

EventBus.on('test', test2)

// EventBus.off('test', test1)
// EventBus.off('test', test2)

setTimeout(() => {
  EventBus.emit('test', 111)
}, 1000)


很nice, 没有发现bug,那就到这了,简单实现了一个事件总线功能。