# 核心事件总线 **本文档引用的文件** - [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` 方法,确保了资源的可管理性,有效防止了内存泄漏,是构建可维护前端应用的理想选择。