eventBus 发布和订阅

eventBus 是一个轻量级的发布/订阅事件总线,主要用于组件之间的松耦合通信。它的原理如下:

  1. 订阅事件:调用 bus.on() 方法订阅某个事件,传入事件名和回调函数。

    1
    2
    3
    eventBus.on('eventName', function(data) {
    // 处理事件逻辑
    })
  2. 发布事件:调用 bus.emit() 方法发布事件,传入事件名和数据。

    1
    eventBus.emit('eventName', data)
  3. eventBus 会将订阅了该事件的回调函数执行,并传入发布事件的数据。一个简单的例子:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    import eventBus from './eventBus.js';

    // 订阅事件
    eventBus.on('eventName', data => {
    console.log(data) // 1
    })

    // 发布事件
    eventBus.emit('eventName', 1)

eventBus.js

eventBus 的代码,用于引入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
class EventBus {
constructor() {
// 初始化一个对象,用来存储不同事件名对应的处理函数数组
this.handlers = {};
}

on(eventName, handler) {
// 注册事件,如果该事件名对应的处理函数数组不存在,创建一个空数组
if (!this.handlers[eventName]) {
this.handlers[eventName] = [];
}
// 将处理函数添加到该事件名对应的数组中
this.handlers[eventName].push(handler);
}

off(eventName, handler) {
// 取消订阅事件
if (!this.handlers[eventName]) {
return;
}
// 从处理函数数组中移除指定的处理函数
this.handlers[eventName] = this.handlers[eventName].filter(h => h !== handler);
}

emit(eventName, ...args) {
// 触发事件,获取该事件名对应的处理函数数组
if (!this.handlers[eventName]) {
return;
}
// 执行每个处理函数,并传入事件data
this.handlers[eventName].forEach(handler => handler(...args));
}

// 只执行一次
once(eventName, handler) {
function onceHandler() {
handler()
this.off(eventName, onceHandler)
}
this.on(eventName, onceHandler)
}
}

const eventBus = new EventBus();

export default eventBus;
// 导出 eventBus 实例

它的工作原理是这样的:

  1. 当调用 on(eventName, handler) 时,handler 函数会被立即注册到 eventBus 中。
  2. 当调用 emit(eventName) 时,eventBus 会立即执行以下操作:
    • 获取 eventName 对应的所有 handler 函数
    • 在当前执行调用栈中,逐个执行这些 handler 函数这意味着 emit 执行的过程中,所有订阅者的函数都会被调用并执行。举个例子:
1
2
3
4
5
6
7
8
9
eventBus.on("hello", () => {
console.log("Hello A")
})

eventBus.on("hello", () => {
console.log("Hello B")
})

eventBus.emit("hello")

执行以上代码后,会立即输出:

1
2
Hello A
Hello B

这是因为 eventBus.emit(“hello”) 执行的时候,直接调用了两个 handler 函数。所以订阅者可以及时收到消息,不是因为 setTimeout 或者定时器,而是 emit 同步执行了所有订阅函数。

once 的原理是:

  1. once() 方法会创建一个内部的 onceHandler 函数
  2. onceHandler 会先执行传入的 handler 函数,然后调用 off() 移除自身
  3. 最终 onceHandler 只会执行一次