基于 TypeScript 的事件驱动架构:当代码能够“相互倾听”
\n\n想象一下,您正在编写一个复杂的 Web 应用:一个即时通讯软件、一个交易机器人或一个服务器监控系统。在某个时刻,您会意识到:简单地按链式调用函数——简直是噩梦。一个模块拖拽着另一个,第三个又依赖于第四个,一旦逻辑发生变化,整个系统就像纸牌屋一样崩塌。
\n\n熟悉吗?那么您需要 基于 TypeScript 的事件驱动架构。这不仅仅是一个时髦的术语,而是一种让代码变得灵活、可扩展且易于理解的切实方法。在本文中,我们将探讨事件驱动编程的工作原理,用 TypeScript 编写我们自己的事件总线,并看看它在哪些场景下能真正派上用场。
\n\n用简单的话说,什么是事件驱动架构
\n\n事件驱动架构 是一种系统组件通过事件进行通信的方法。一个模块生成一个事件(例如,“用户已注册”),其他模块订阅该事件并做出响应。它们之间没有直接耦合。
\n\n听起来很抽象?让我们举一个生活中的例子。想象一个办公室:
\n- \n
- 传统方法:经理亲自走到每个员工面前说:“做一份报告”。 \n
- 事件驱动:经理喊道:“需要一份报告!”——然后所有会做报告的人自己开始工作。 \n
在第二种情况下,经理不需要知道具体谁会做报告。他只需发送一个事件。而员工可以自由加入或退出,而无需更改经理的逻辑。这就是 事件驱动编程 的本质。
\n\n为什么 TypeScript 是事件驱动架构的理想选择
\n\nJavaScript 本身是事件驱动的(回想一下 addEventListener),但在大型项目中,没有类型会导致混乱。TypeScript 事件 解决了这个问题:您确切地知道事件中携带了哪些数据,并且编译器会在开发阶段捕获错误,而不是在运行时。
此外,TypeScript 支持泛型、重载和回调的严格类型化——这些都是创建可靠 事件总线 所必需的。
\n\n从零开始用 TypeScript 编写事件总线
\n\n让我们从一个简单但实用的例子开始。创建一个 EventBus 类——一个中央事件总线,所有模块将通过它进行通信。
// event-bus.ts\ntype EventHandler<T = any> = (data: T) => void;\n\nclass EventBus {\n private handlers: Map<string, EventHandler[]> = new Map();\n\n // 订阅事件\n on<T>(event: string, handler: EventHandler<T>): void {\n if (!this.handlers.has(event)) {\n this.handlers.set(event, []);\n }\n this.handlers.get(event)!.push(handler as EventHandler);\n }\n\n // 取消订阅事件\n off(event: string, handler: EventHandler): void {\n const handlers = this.handlers.get(event);\n if (handlers) {\n this.handlers.set(event, handlers.filter(h => h !== handler));\n }\n }\n\n // 触发事件\n emit<T>(event: string, data: T): void {\n const handlers = this.handlers.get(event);\n if (handlers) {\n handlers.forEach(handler => handler(data));\n }\n }\n}\n\nexport const globalEventBus = new EventBus();\n\n就这样,我们编写了一个简单的 基于 TypeScript 的事件驱动架构。现在任何模块都可以导入 globalEventBus,并监听或生成事件。
使用示例:通知与日志记录
\n\n假设我们有一个认证模块和一个通知模块。它们不应该直接知道彼此的存在。
\n\n// auth-module.ts\nimport { globalEventBus } from './event-bus';\n\nexport function loginUser(email: string): void {\n // ... 登录逻辑\n console.log(`用户