TypeScript 中的事件总线与观察者模式
观察者模式(Observer)及其实际实现——事件总线(Event Bus)——是构建低耦合、可扩展应用程序的基础工具。在本文中,我们将详细讲解如何在 TypeScript 中使用强类型实现事件总线,并探讨其在教育项目中的应用。
什么是观察者模式?
观察者模式定义了对象之间的一对多关系。当一个对象(发布者,subject)改变其状态时,所有依赖它的对象(观察者,observers)都会自动收到通知并更新。这可以避免组件之间的紧密耦合。
什么是事件总线?
事件总线(Event Bus)是一种实现观察者模式的集中式机制。组件不直接让观察者订阅发布者,而是通过一个统一的总线进行通信。组件可以:
- 订阅(subscribe/listen)特定事件。
- 发布(emit/publish)事件,该事件将传递给所有订阅者。
- 取消订阅(unsubscribe)事件,当不再需要时。
这种方法非常适合:
- 互不知晓的模块之间的交互。
- 处理用户操作(点击、数据输入)。
- 响应应用程序状态的变化(数据加载、错误)。
在 TypeScript 中实现事件总线
TypeScript 添加了强大的类型系统,使事件总线更安全、更可预测。我们将创建一个类型化的事件总线,其中每个事件都有严格定义的参数集。
1. 定义事件类型
首先,为事件名称及其参数定义一个映射接口:
// 定义事件映射:键是事件名称,值是参数类型interface EventMap { 'user:login': { userId: string; timestamp: number }; 'data:loaded': { items: string[] }; 'error:occurred': { message: string; code: number }; // 可以添加任何其他事件}2. 创建 EventBus 类
现在,使用泛型创建类本身,以实现强类型:
type Listener<T> = (payload: T) => void;
class EventBus<T extends Record<string, unknown>> { private listeners: Map<keyof T, Set<Listener<unknown>>> = new Map();
// 订阅事件 subscribe<K extends keyof T>(event: K, listener: Listener<T[K]>): () => void { if (!this.listeners.has(event)) { this.listeners.set(event, new Set()); } const currentListeners = this.listeners.get(event)!; currentListeners.add(listener as Listener<unknown>);
// 返回取消订阅的函数 return () => { currentListeners.delete(listener as Listener<unknown>); }; }
// 发布事件 emit<K extends keyof T>(event: K, payload: T[K]): void { const currentListeners = this.listeners.get(event); if (currentListeners) { currentListeners.forEach((listener) => { listener(payload); }); } }
// 取消所有订阅(用于清理) clear(): void { this.listeners.clear(); }}
// 使用我们的事件映射创建总线实例export const appEventBus = new EventBus<EventMap>();3. 使用示例
让我们看看不同模块如何通过事件总线进行交互:
// 认证模块class AuthService { login(userId: string) { // ... 登录逻辑 ... appEventBus.emit('use