Appearance
18. 事件总线 EventBus
自定义一个事件总线。
熟悉 vue 的同学一定知道 vue2 使用事件总线来进行非父子关系组件之间的信息共享和传递。所有组件共用相同的事件中心,可以向该中心注册发送事件或接收事件,所以组件都可以上下平
实际上,自定义事件总线属于一种 发布-订阅模式,其中包括三个角色:
- 发布者(Publisher):发出事件(Event)
- 订阅者(Subscriber):订阅事件(Event),并且会进行响应(Handler)
- 事件总线(EventBus):无论是发布者还是订阅者都是通过事件总线作为中台的
手写一个事件总线,至少实现以下方法:
- 事件的监听方法
on
- 事件的发射方法
emit
- 事件的取消监听
off
【基本思路】 订阅者通过 on
订阅事件,将相应事件(handler)都添加到事件总线的一个数组中。 发布者通过 emit
发射事件,触发事件事件总线中相关的响应事件执行。
【数据结构】 不同订阅者的事件不同,监听的每一个事件 evenName
都映射了一个事件数组 handlers
,该数组是多次监听同一个事件的响应事件 handler
的集和。
【代码实现】
js
class EventBus {
constructor() {
this.eventBus = {}
}
// 订阅者订阅事件,将响应事件添加到事件总线中心
on(eventName, eventCallback, thisArg) {
const handlers = this.eventBus[eventName]
if (!handlers) {
this.eventBus[eventName] = []
}
this.eventBus[eventName].push({ eventCallback, thisArg })
}
// 发布者发送事件,触发事件中心中的响应事件执行
emit(eventName, ...payload) {
const handlers = this.eventBus[eventName]
if (!handlers) return
handlers.forEach(handler => {
handler.eventCallback.apply(handler.thisArg, payload)
})
}
// 取消订阅,取消某一事件的响应事件
off(eventName, eventCallback) {
const handlers = this.eventBus[eventName]
const tempHandlers = [...handlers]
for (let i = 0; i < tempHandlers.length; i++) {
if (tempHandlers[i].eventCallback === eventCallback) {
handlers.splice(i, 1)
break
}
}
}
// 清除某一个订阅事件
clear(eventName) {
if (!this.eventBus[eventName]) return
this.eventBus[eventName] = []
}
}
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
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
测试:
js
const eventBus = new EventBus()
// 订阅事件 func
const handler1 = function (x, y) {
console.log('第 1 次监听到了', this, x, y)
}
eventBus.on('func', handler1, { name: 'murphy' })
// 订阅事件 func
const handler2 = function (x) {
console.log('第 2 次监听到了', this, x)
}
eventBus.on('func', handler2, { name: 'murphy' })
// 发布事件
eventBus.emit('func', '参数1', '参数2')
// 取消订阅
eventBus.off('func', handler1)
eventBus.emit('func', '参数1', '参数2')
// 清除订阅
eventBus.clear('func')
eventBus.emit('func', '参数1', '参数2')
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
TIP
- vue2 中实现了事件总线,可以直接使用。
- vue3 为了保持 vue 框架的纯粹性,移除了事件总线,推荐使用 mitt