惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

WordPress大学
WordPress大学
Spread Privacy
Spread Privacy
T
The Exploit Database - CXSecurity.com
Simon Willison's Weblog
Simon Willison's Weblog
P
Privacy & Cybersecurity Law Blog
L
LINUX DO - 热门话题
T
Threat Research - Cisco Blogs
T
Tenable Blog
TaoSecurity Blog
TaoSecurity Blog
Security Archives - TechRepublic
Security Archives - TechRepublic
AI
AI
P
Proofpoint News Feed
A
About on SuperTechFans
P
Privacy International News Feed
月光博客
月光博客
雷峰网
雷峰网
S
Secure Thoughts
博客园 - 叶小钗
博客园 - 聂微东
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
Project Zero
Project Zero
The Cloudflare Blog
SecWiki News
SecWiki News
The Hacker News
The Hacker News
V
Vulnerabilities – Threatpost
罗磊的独立博客
A
Arctic Wolf
阮一峰的网络日志
阮一峰的网络日志
Know Your Adversary
Know Your Adversary
酷 壳 – CoolShell
酷 壳 – CoolShell
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
T
Troy Hunt's Blog
The Last Watchdog
The Last Watchdog
Schneier on Security
Schneier on Security
小众软件
小众软件
有赞技术团队
有赞技术团队
博客园 - 司徒正美
T
Tailwind CSS Blog
量子位
C
Cybersecurity and Infrastructure Security Agency CISA
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
Hugging Face - Blog
Hugging Face - Blog
人人都是产品经理
人人都是产品经理
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
S
Security @ Cisco Blogs
大猫的无限游戏
大猫的无限游戏
S
SegmentFault 最新的问题
Apple Machine Learning Research
Apple Machine Learning Research
宝玉的分享
宝玉的分享
L
Lohrmann on Cybersecurity

Anran758's blog

Promise 与异步编程 数据结构实践 MySQL 实践 Redux 食用指南 计算机网络原理笔记 Accessibility Parsing 无障碍页面分析 软件工程笔记 JavaScript 实现二叉树 闭包与链式设计的使用示例 React 知识回顾 (优化篇) React 知识回顾 (使用篇) Hexo 常见问题解决方案 React vs Vue webpack + Travis CI 自动部署项目应用 从零构建 webpack 脚手架(基础篇) Flexbox 布局实际用例 Flexbox 布局入门 Hexo blog 的升级与同步方案 将 JSON 数据格式输出至页面上
组件通信: EventBus 的原理解析与应用
本文作者: anran758 · 2022-01-22 · via Anran758's blog

在开发复杂的单页面应用时,我们经常会遇到一个问题:如何高效地在组件或模块之间进行通信?这里,EventBus(事件总线)就派上了用场。简单来说,EventBus 是一种设计模式,它允许不同组件或模块之间通过事件来通信,而无需直接引用彼此。

EventBus 是传统的组件通信解决方案,下面我们将讲解 EventBus 跨组件通信的原理、实现方式以及该如何使用。

原理解析

EventBus 的核心在于提供一个中央机制,允许不同的组件或模块相互通信,而不必直接引用对方。它是一种典型的发布-订阅(pub-sub)模式,这是一种广泛使用的设计模式,用于解耦发送者和接收者。

在这个模式中,EventBus 充当了一个中介的角色:它允许组件订阅那些它们感兴趣的事件,并在这些事件发生时接收通知。同样,当某个事件发生时,比如用户的一个动作或者数据的变化,EventBus 负责将这一消息广播给所有订阅了该事件的组件。

它基于三个核心操作:注册事件(on(event, callback))、触发事件(emit(event, ...args))、以及移除事件(off(event, callback))。因此,EventBus 的基本代码可以看到:

1
2
3
4
5
6
7
8
9
10
11
12
13
class EventBus {
on(event, callback) {

}

emit(event, ...args) {

}

off(event, callback) {

}
}

显然,我们需要有一个私有变量来储存用户的函数,此时为类添加 events 属性。events 属性是一个对象映射,其中每个属性表示一个事件名称,对应的值是一个回调函数的数组,这个数组存储了所有订阅了该事件的回调函数。

1
2
3
4
class EventBus {
private events: Record<string, Function[]> = {};

}

当用户执行订阅事件 on 时,回调函数会被添加到相应事件名称的数组中。这样,同一个事件可以被不同组件或模块订阅,而每个订阅者的回调函数都会被正确地保存在事件队列中。最后,当触发事件 emit 时,事件队列中的每个回调函数都会被执行,实现了事件的触发和通知功能。若已经没有订阅需求,则可以通过 off 移除已经订阅的事件。

代码实现

接下来我们按照前文所述完善我们的代码实现:

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
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
class EventBus {

private events: Record<string, Function[]> = {};







public on(eventName: string, callback: Function): this {

if (typeof callback !== "function") {
throw new Error("EventBus 'on' method expects a callback function.");
}


if (!this.events[eventName]) {
this.events[eventName] = [];
}


this.events[eventName].push(callback);


return this;
}







public emit(eventName: string, ...args: any[]): this {

const callbacks = this.events[eventName];
if (callbacks) {

callbacks.forEach((callback) => callback(...args));
}


return this;
}







public off(event?: string | string[], callback?: Function): this {

if (!event || (Array.isArray(event) && !event.length)) {
this.events = {};
return this;
}


if (Array.isArray(event)) {
event.forEach((e) => this.off(e, callback));
return this;
}


if (!callback) {
delete this.events[event];
return this;
}


const callbacks = this.events[event];
if (callbacks) {
const index = callbacks.indexOf(callback);
if (index > -1) {
callbacks.splice(index, 1);
}
}


return this;
}
}

当涉及到一次性的事件监听需求时,我们可以进一步扩展 EventBus,以支持一次性事件监听。允许用户在某个事件触发后,自动移除事件监听器,以确保回调函数只执行一次:

1
2
3
4
5
6
7
8
9
10
11
12
13
class EventBus {

public once(eventName: string, callback: Function): this {
const onceWrapper = (...args: any[]) => {
this.off(eventName, onceWrapper);
callback(...args);
};

this.on(eventName, onceWrapper);

return this;
}
}

使用方式

我们将类的封装到 event-bus.ts 中,通过模块的来管理:

1
2
3
export class EventBus {

}

我们现在已经封装好了一个类,若我们像使用则需要实例化。此处再文件内直接实例化一个类:

1
2

export const eventBus = new EventBus();

这样使用时可以提供两种方式:

  1. 引入已经实例化的 eventBus

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    import { eventBus } from './event-bus';


    eventBus.on('eventName', callback);


    eventBus.emit('eventName', data);


    eventBus.off('eventName', callback);
  2. 需要多个独立的事件总线实例时,或者希望在不同模块或组件之间使用不同的事件总线时,可以选择额外实例化 eventBus。这样做的目的可能是为了隔离命名的冲突、组件与模块逻辑隔离等原因。

    1
    2
    3
    4
    5
    6

    import { EventBus } from './event-bus';


    export const eventBusA = new EventBus();
    export const eventBusB = new EventBus();
    1
    2
    3
    4
    5
    6
    7
    8
    9
    import {eventBusA, eventBusB} from './events'


    eventBusA.on('eventA', callbackA);
    eventBusB.on('eventB', callbackB);


    eventBusA.emit('eventA', dataA);
    eventBusB.emit('eventB', dataB);

以下是 CodeSandbox 的演示代码:

总结

在本文中,我们深入探讨了 EventBus 的原理,了解了它是如何工作的。我们学习了它的核心操作。除了本文所提及的实现方式,有时候在生产项目中,为了确保代码的可靠性,我们可以考虑使用成熟的第三方库,例如 mitttiny-emitter

这些库已经经过广泛的测试和使用,可以提供稳定和可靠的 EventBus 功能。