# 核心事件总线
**本文档引用的文件**
- [EventBuilderImpl.ts](file://src/events/impl/EventBuilderImpl.ts)
- [IEventBuilder.ts](file://src/events/IEventBuilder.ts)
- [IDestroyable.ts](file://src/common/types/IDestroyable.ts)
- [EventManager.ts](file://src/events/EventManager.ts)
- [DesktopEventManager.ts](file://src/events/DesktopEventManager.ts)
- [WindowFormEventManager.ts](file://src/events/WindowFormEventManager.ts)
## 目录
1. [简介](#简介)
2. [核心数据结构设计](#核心数据结构设计)
3. [泛型类型安全机制](#泛型类型安全机制)
4. [事件监听器管理](#事件监听器管理)
- [addEventListener 方法详解](#addeventlistener-方法详解)
- [removeEventListener 方法详解](#removeeventlistener-方法详解)
5. [事件通知与分发](#事件通知与分发)
6. [资源清理与内存泄漏防范](#资源清理与内存泄漏防范)
7. [实际应用示例](#实际应用示例)
8. [总结](#总结)
## 简介
`EventBuilderImpl` 类是本系统事件机制的核心实现,提供了一个类型安全、功能完整的事件总线系统。该类实现了 `IEventBuilder` 接口,遵循发布-订阅模式,支持事件的注册、移除和触发,并具备立即执行、一次性监听等高级特性。通过继承 `IDestroyable` 接口,它还提供了资源清理能力,有效防止内存泄漏。
该事件系统被多个模块广泛使用,包括全局事件管理器(`eventManager`)、桌面事件管理器(`desktopEM`)和窗口表单事件管理器(`wfem`),构成了整个应用的通信骨架。
**Section sources**
- [EventBuilderImpl.ts](file://src/events/impl/EventBuilderImpl.ts#L7-L95)
- [IEventBuilder.ts](file://src/events/IEventBuilder.ts#L13-L46)
- [IDestroyable.ts](file://src/common/types/IDestroyable.ts#L4-L7)
## 核心数据结构设计
`EventBuilderImpl` 采用 `Map` 和 `Set` 的组合来高效管理事件监听器,这种设计在性能和内存使用上达到了良好的平衡。
其核心是一个私有成员 `_eventHandlers`,其类型为 `Map>>`。这个数据结构的含义是:
- **外层 Map**:以事件名称(`keyof Events`)作为键,确保每个事件名对应一个独立的监听器集合。
- **内层 Set**:存储特定事件的所有监听器包装对象(`HandlerWrapper`)。使用 `Set` 而非数组可以天然避免重复添加同一个监听函数,并且插入和删除操作的时间复杂度为 O(1)。
`HandlerWrapper` 是一个简单的接口,包含两个属性:`fn`(原始的监听函数)和 `once`(布尔值,标记是否为一次性监听器)。这种包装方式将业务逻辑(函数本身)与元数据(如 `once` 标志)分离,使得事件管理更加灵活。
```mermaid
classDiagram
class EventBuilderImpl~Events~ {
-_eventHandlers : Map~keyof Events, Set~HandlerWrapper~Events[keyof Events]~~~
+addEventListener()
+removeEventListener()
+notifyEvent()
+destroy()
}
class HandlerWrapper~T~ {
+fn : T
+once : boolean
}
EventBuilderImpl --> "0..*" HandlerWrapper : 包含
```
**Diagram sources**
- [EventBuilderImpl.ts](file://src/events/impl/EventBuilderImpl.ts#L7-L15)
**Section sources**
- [EventBuilderImpl.ts](file://src/events/impl/EventBuilderImpl.ts#L7-L15)
## 泛型类型安全机制
`EventBuilderImpl` 类通过 TypeScript 的泛型系统实现了严格的类型安全。其定义为 `class EventBuilderImpl`,其中 `Events` 是一个必须符合 `IEventMap` 接口约束的泛型类型。
`IEventMap` 接口定义了事件映射的基本结构:一个索引签名 `[key: string]: (...args: any[]) => void`,表示键是字符串类型的事件名,值是任意参数的无返回值函数。
当实例化 `EventBuilderImpl` 时,需要传入一个具体的事件接口。例如,在 `WindowFormEventManager.ts` 中定义了 `IWindowFormEvent` 接口,并将其作为泛型参数:
```typescript
export const wfem = new EventBuilderImpl()
```
这种设计带来了以下优势:
1. **编译时检查**:在调用 `addEventListener` 或 `notifyEvent` 时,TypeScript 编译器会根据 `IWindowFormEvent` 的定义检查事件名和参数类型是否正确。
2. **智能提示**:开发者在编写代码时能获得准确的事件名和参数类型提示。
3. **防止错误**:无法订阅未在接口中定义的事件,也无法传递错误类型的参数。
**Section sources**
- [IEventBuilder.ts](file://src/events/IEventBuilder.ts#L4-L7)
- [WindowFormEventManager.ts](file://src/events/WindowFormEventManager.ts#L5-L59)
## 事件监听器管理
### addEventListener 方法详解
`addEventListener` 方法用于向事件总线注册新的监听器。它接收三个参数:事件名、处理函数和可选的配置项。
#### 配置项行为逻辑
- **immediate (立即执行)**:如果设置为 `true`,则在添加监听器后立即同步执行一次该函数。
- **immediateArgs (立即执行参数)**:为 `immediate` 执行阶段提供参数。若未指定,则使用空数组。
- **once (一次性监听)**:如果设置为 `true`,则该监听器在第一次被触发后自动从事件队列中移除。
#### 实现细节
1. **空值检查**:首先检查 `handler` 是否存在,避免无效监听器。
2. **惰性初始化**:如果这是该事件的第一个监听器,则创建一个新的 `Set` 来存放后续的 `HandlerWrapper`。
3. **去重机制**:在添加前,通过遍历 `Set` 并比较 `wrapper.fn === handler` 来确保不会重复添加相同的函数引用。
4. **异常捕获**:在执行 `immediate` 回调时,使用 `try-catch` 捕获任何可能抛出的异常,并将其输出到控制台,防止因单个监听器的错误而中断主流程。
```mermaid
flowchart TD
Start([开始]) --> CheckHandler["检查 handler 是否为空"]
CheckHandler --> |否| InitSet["检查事件对应的 Set 是否存在"]
InitSet --> |否| CreateSet["创建新的 Set"]
CreateSet --> GetSet["获取事件对应的 Set"]
InitSet --> |是| GetSet
GetSet --> CheckDuplicate["检查是否已存在相同 handler"]
CheckDuplicate --> |否| AddWrapper["添加 HandlerWrapper 到 Set"]
AddWrapper --> CheckImmediate["检查 immediate 选项"]
CheckImmediate --> |是| ExecuteNow["立即执行 handler"]
ExecuteNow --> End([结束])
CheckImmediate --> |否| End
CheckHandler --> |是| End
CheckDuplicate --> |是| End
```
**Diagram sources**
- [EventBuilderImpl.ts](file://src/events/impl/EventBuilderImpl.ts#L20-L46)
**Section sources**
- [EventBuilderImpl.ts](file://src/events/impl/EventBuilderImpl.ts#L20-L46)
### removeEventListener 方法详解
`removeEventListener` 方法负责从事件队列中移除指定的监听器。
其实现的关键在于**引用比对去重机制**。由于监听器是以 `HandlerWrapper` 对象的形式存储在 `Set` 中的,直接比较 `Set` 中的对象与传入的 `handler` 函数是不相等的。因此,该方法会遍历 `Set` 中的每一个 `wrapper`,并使用 `wrapper.fn === handler` 来精确匹配原始的函数引用。
一旦找到匹配项,就调用 `set.delete(wrapper)` 将其从 `Set` 中移除。这种基于引用的比对确保了只有完全相同的函数实例才会被移除,保证了操作的准确性。
**Section sources**
- [EventBuilderImpl.ts](file://src/events/impl/EventBuilderImpl.ts#L55-L64)
## 事件通知与分发
`notifyEvent` 方法是事件系统的触发点,负责通知所有订阅了特定事件的监听器。
其工作流程如下:
1. **存在性检查**:首先检查是否存在该事件名对应的监听器集合,若不存在则直接返回。
2. **批量通知**:遍历该事件名对应 `Set` 中的所有 `HandlerWrapper`。
3. **异常捕获**:在调用每个监听器的 `fn(...args)` 时,使用 `try-catch` 块包裹,确保单个监听器的错误不会影响其他监听器的执行。
4. **once 监听器清理**:在成功调用一个监听器后,检查其 `once` 标志。如果为 `true`,则立即将其从 `Set` 中删除,实现一次性监听的功能。
这种“先通知,后清理”的策略保证了即使在 `once` 监听器执行过程中有其他代码尝试移除它,也不会产生竞态条件。
```mermaid
sequenceDiagram
participant Publisher as 事件发布者
participant EventBus as EventBuilderImpl
participant ListenerA as 监听器 A (once : true)
participant ListenerB as 监听器 B (once : false)
Publisher->>EventBus : notifyEvent('click', x, y)
EventBus->>ListenerA : 执行 fn(x, y)
ListenerA-->>EventBus : 完成
EventBus->>EventBus : 检查 wrapper.once == true
EventBus->>EventBus : 从 Set 中删除 ListenerA
EventBus->>ListenerB : 执行 fn(x, y)
ListenerB-->>EventBus : 完成
EventBus-->>Publisher : 通知完成
```
**Diagram sources**
- [EventBuilderImpl.ts](file://src/events/impl/EventBuilderImpl.ts#L75-L90)
**Section sources**
- [EventBuilderImpl.ts](file://src/events/impl/EventBuilderImpl.ts#L75-L90)
## 资源清理与内存泄漏防范
`EventBuilderImpl` 通过多种策略有效防范内存泄漏:
1. **显式销毁接口**:通过实现 `IDestroyable` 接口,提供了 `destroy()` 方法。调用此方法会清空 `_eventHandlers` Map,释放所有对监听器函数的引用,使它们可以被垃圾回收器回收。这对于长生命周期的应用或动态创建/销毁的组件至关重要。
2. **监听器去重**:`addEventListener` 方法中的去重逻辑防止了同一函数被多次添加,避免了不必要的内存占用和重复执行。
3. **once 监听器自动清理**:`once` 选项确保了一次性监听器在执行后立即被移除,无需手动清理。
4. **异常隔离**:`try-catch` 机制保证了事件分发过程的健壮性,防止因监听器内部错误导致整个事件系统崩溃。
**Section sources**
- [EventBuilderImpl.ts](file://src/events/impl/EventBuilderImpl.ts#L92-L94)
- [IDestroyable.ts](file://src/common/types/IDestroyable.ts#L4-L7)
## 实际应用示例
以下是 `EventBuilderImpl` 在项目中的具体应用实例:
- **全局事件管理器**:在 `EventManager.ts` 中,`eventManager` 实例被用来管理认证状态改变 (`onAuthChange`) 和主题切换 (`onThemeChange`) 等全局事件。
- **桌面事件管理器**:在 `DesktopEventManager.ts` 中,`desktopEM` 实例用于响应桌面应用图标位置的变化 (`desktopAppPosChange`)。
- **窗口表单事件管理器**:在 `WindowFormEventManager.ts` 中,`wfem` 实例监控窗口的最小化、最大化、关闭等生命周期事件。
这些预定义的事件管理器实例化了 `EventBuilderImpl`,并通过具体的事件接口(如 `IWindowFormEvent`)锁定了可用的事件集,为不同模块提供了清晰、类型安全的通信契约。
**Section sources**
- [EventManager.ts](file://src/events/EventManager.ts#L3-L34)
- [DesktopEventManager.ts](file://src/events/DesktopEventManager.ts#L3-L15)
- [WindowFormEventManager.ts](file://src/events/WindowFormEventManager.ts#L3-L60)
## 总结
`EventBuilderImpl` 作为一个轻量级但功能完备的事件总线实现,通过精心设计的数据结构(`Map`+`Set`)和 TypeScript 泛型,实现了高性能、类型安全的事件管理。其 `addEventListener`、`removeEventListener` 和 `notifyEvent` 方法构成了一个健壮的发布-订阅循环,而 `immediate`、`once` 等选项以及异常捕获机制则增强了其实用性和可靠性。最后,通过 `IDestroyable` 接口提供的 `destroy` 方法,确保了资源的可管理性,有效防止了内存泄漏,是构建可维护前端应用的理想选择。