VTJ核心引擎:事件发射器

3 阅读6分钟

事件发射器

简介

本文件系统性地梳理并说明事件发射器在项目中的设计与实现,覆盖事件订阅、发布、取消订阅的 API 使用方式,并结合真实业务模型(项目、区块、节点、历史)展示事件驱动的典型模式。同时给出错误处理与性能优化建议,帮助开发者在复杂交互场景中构建稳定、可维护的事件驱动体系。

项目结构

事件发射器位于核心包中,通过一个统一的发射器实例对外提供事件能力;底层基于 mitt 库封装,确保类型安全与易用性。事件常量与事件负载类型分布在各模型文件中,形成“模型变更即发事件”的约定式设计。

graph TB
subgraph "核心工具层"
EmitterTS["packages/core/src/tools/emitter.ts"]
EmitterDTS["packages/core/types/tools/emitter.d.ts"]
end
subgraph "基础封装层"
MittTS["packages/base/src/mitt.ts"]
MittDTS["packages/base/types/mitt.d.ts"]
end
subgraph "模型层"
ProjectTS["packages/core/src/models/project.ts"]
BlockTS["packages/core/src/models/block.ts"]
NodeTS["packages/core/src/models/node.ts"]
HistoryTS["packages/core/src/models/history.ts"]
EventModelTS["packages/core/src/models/event.ts"]
end
subgraph "使用方"
DesignerEngine["packages/designer/src/framework/engine.ts"]
end
MittTS --> EmitterTS
MittDTS --> EmitterDTS
EmitterTS --> ProjectTS
EmitterTS --> BlockTS
EmitterTS --> NodeTS
EmitterTS --> HistoryTS
EmitterTS --> EventModelTS
DesignerEngine --> EmitterTS

核心组件

  • 统一事件发射器实例:在核心工具层导出一个强类型的发射器实例,绑定到具体事件映射表,保证事件名与负载类型一一对应。
  • 类型化事件负载:通过事件常量与事件负载接口,确保事件携带的数据结构清晰、可推断。
  • 事件驱动的模型:项目、区块、节点、历史等模型在关键状态变更处主动发出事件,形成“模型变更即发事件”的契约。

架构总览

事件发射器采用“集中式发射器 + 分层模型”的架构:

  • 工具层:提供统一发射器实例与类型约束。
  • 模型层:在模型内部直接触发事件,保持低耦合。
  • 使用方:订阅事件,响应模型变化,执行业务逻辑。
sequenceDiagram
participant Model as "模型(如 Project/Block/Node/History)"
participant Emitter as "发射器(emitter)"
participant Listener as "监听者(业务模块)"
Model->>Emitter : "emit(事件名, 负载)"
Emitter-->>Listener : "回调执行"
Note over Model,Listener : "模型变更即发事件,监听者按需处理"

详细组件分析

组件A:事件发射器实例与类型

  • 发射器实例:基于 mitt 的泛型发射器,绑定事件映射表,确保事件名与负载类型一致。
  • 类型导出:导出 on/off/emit/all.clear 等常用方法签名,便于上层直接使用。
  • 事件类型:定义模型事件类型集合,覆盖 create/update/delete/clone/clear/load/publish/gen 等。
classDiagram
class Emitter {
+on(type, listener) void
+off(type, listener) void
+emit(type, ...args) void
+all.clear() void
}
class EventsMap {
+EVENT_PROJECT_CHANGE : ProjectModelEvent
+EVENT_PROJECT_ACTIVED : ProjectModelEvent
+EVENT_PROJECT_DEPS_CHANGE : ProjectModelEvent
+EVENT_PROJECT_PAGES_CHANGE : ProjectModelEvent
+EVENT_PROJECT_BLOCKS_CHANGE : ProjectModelEvent
+EVENT_PROJECT_APIS_CHANGE : ProjectModelEvent
+EVENT_PROJECT_META_CHANGE : ProjectModelEvent
+EVENT_PROJECT_PUBLISH : ProjectModelEvent
+EVENT_PROJECT_FILE_PUBLISH : ProjectModelEvent
+EVENT_BLOCK_CHANGE : BlockModel
+EVENT_NODE_CHANGE : NodeModel
+EVENT_HISTORY_CHANGE : HistoryModelEvent
+EVENT_HISTORY_LOAD : HistoryModelEvent
+EVENT_PROJECT_GEN_SOURCE : ProjectModelEvent
}
Emitter --> EventsMap : "类型约束"

组件B:项目模型事件

  • 事件常量:涵盖项目变更、激活/关闭、依赖变更、页面/区块/API/Meta 变更、发布、出码等。
  • 事件负载:包含模型实例、事件类型、数据载荷。
  • 触发时机:在更新、新增、删除、克隆、移动、保存为区块等关键操作后触发相应事件。
sequenceDiagram
participant Project as "ProjectModel"
participant Emitter as "emitter"
participant Listener as "订阅者"
Project->>Emitter : "emit(EVENT_PROJECT_*, 负载)"
Emitter-->>Listener : "回调执行"
Note over Project,Listener : "例如 : 更新页面/区块/API/Meta、依赖变更、发布等"

组件C:区块模型事件

  • 事件常量:区块变更事件名。
  • 事件负载:区块模型实例。
  • 触发时机:在更新、设置/移除函数、状态、CSS、watch、prop、emit、slot、inject、数据源、节点增删改、锁定/解锁等操作后触发。
sequenceDiagram
participant Block as "BlockModel"
participant Emitter as "emitter"
participant Listener as "订阅者"
Block->>Emitter : "emit(EVENT_BLOCK_CHANGE, this)"
Emitter-->>Listener : "回调执行"
Note over Block,Listener : "例如 : 更新属性、添加/删除节点、设置状态/CSS/watch 等"

组件D:节点模型事件

  • 事件常量:节点变更事件名。
  • 事件负载:节点模型实例。
  • 触发时机:在更新、设置/移除子节点、插槽、属性、事件、指令等操作后触发。
sequenceDiagram
participant Node as "NodeModel"
participant Emitter as "emitter"
participant Listener as "订阅者"
Node->>Emitter : "emit(EVENT_NODE_CHANGE, this)"
Emitter-->>Listener : "回调执行"
Note over Node,Listener : "例如 : 设置属性/事件/指令、增删子节点、可见性切换等"

组件E:历史模型事件

  • 事件常量:历史变更与加载事件名。
  • 事件负载:包含模型实例、事件类型、数据载荷(如删除/创建的 ID 列表、加载的历史项)。
  • 触发时机:在新增、更新、删除、前进/后退、加载、清空等操作后触发相应事件。
sequenceDiagram
participant History as "HistoryModel"
participant Emitter as "emitter"
participant Listener as "订阅者"
History->>Emitter : "emit(EVENT_HISTORY_CHANGE|EVENT_HISTORY_LOAD, 负载)"
Emitter-->>Listener : "回调执行"
Note over History,Listener : "例如 : 新增历史项、删除历史项、加载某历史项等"

组件F:事件模型(节点事件)

  • 事件模型:对节点事件进行解析与序列化,支持事件处理器与修饰符管理。
  • 用途:配合事件发射器,将节点事件转换为 DSL 或反向解析,保证事件在模型与 UI 间的一致性。
flowchart TD
Start(["事件解析入口"]) --> Parse["解析事件对象"]
Parse --> Build["构建事件模型(EventModel)"]
Build --> ToDSL["序列化为DSL"]
ToDSL --> End(["返回事件DSL"])

组件G:事件使用示例(设计器引擎)

  • 场景:在设计器引擎中根据历史模型事件保存/加载历史项,并触发视图刷新。
  • 流程:监听历史事件 -> 保存/删除历史项 -> 保存历史 -> 触发视图刷新。
sequenceDiagram
participant Engine as "Designer Engine"
participant History as "HistoryModel"
participant Service as "存储服务"
participant View as "视图"
Engine->>History : "触发历史事件"
History->>Service : "保存/删除历史项"
History->>Service : "保存历史"
History->>View : "触发视图刷新"
Note over Engine,View : "事件驱动的持久化与渲染流程"

依赖关系分析

  • 发射器依赖:核心工具层依赖基础封装层提供的 mitt 封装,确保类型安全与一致的导出。
  • 模型依赖:各模型直接依赖发射器,形成“模型内部自触发”的事件模式,避免跨层耦合。
  • 使用方依赖:设计器引擎等上层模块仅订阅事件,不直接修改模型内部状态,符合单向数据流。
graph LR
MittTS["mitt.ts"] --> EmitterTS["emitter.ts"]
EmitterTS --> ProjectTS["project.ts"]
EmitterTS --> BlockTS["block.ts"]
EmitterTS --> NodeTS["node.ts"]
EmitterTS --> HistoryTS["history.ts"]
EmitterTS --> EventModelTS["event.ts"]
DesignerEngine["engine.ts"] --> EmitterTS

性能考量

  • 事件粒度控制:在高频操作(如拖拽、批量更新)中,优先使用“静默更新”(silent 参数),在合适时机一次性触发事件,减少事件风暴。
  • 事件去抖/节流:对于频繁触发的 UI 事件(如滚动、缩放),可在监听侧做去抖/节流,降低回调频率。
  • 事件清理:及时取消不再使用的监听器,避免内存泄漏;对全局事件应建立统一的注销入口。
  • 负载大小:事件负载尽量轻量化,必要时传递引用而非深拷贝;对大对象变更,考虑只传递变更摘要。
  • 并发与顺序:避免在事件回调中进行阻塞操作;若存在串行依赖,使用队列或 Promise 链保证顺序。

故障排查指南

  • 事件未触发
    • 检查模型是否正确调用发射器的 emit 方法。
    • 确认事件常量拼写与订阅一致。
    • 排查静默更新导致的事件缺失。
  • 回调未执行
    • 确认监听器已正确注册且未被提前注销。
    • 检查监听器作用域与生命周期,避免异步回调失效。
  • 内存泄漏
    • 确保在组件卸载或上下文销毁时调用 off 或统一清理。
    • 对于全局事件,建立统一的注销清单。
  • 负载类型不匹配
    • 使用强类型发射器与事件映射,确保事件名与负载类型一致。
    • 在订阅侧进行必要的类型校验与解构。

结论

该事件发射器通过“集中式发射器 + 类型化事件映射 + 模型内自触发”的设计,实现了清晰、可维护、可扩展的事件驱动架构。结合项目、区块、节点、历史等模型的事件契约,开发者可以快速构建响应式与可追踪的交互体系。遵循本文的使用规范与性能建议,可在复杂场景中保持系统的稳定性与可演进性。

附录

  • API 一览(基于发射器导出)
    • on(type: string, listener: (...args: any[]) => void): void
    • off(type: string, listener: (...args: any[]) => void): void
    • emit(type: string, ...args: any[]): void
    • all: { clear(): void }
  • 常用事件名(示例)
    • 项目:EVENT_PROJECT_CHANGE、EVENT_PROJECT_ACTIVED、EVENT_PROJECT_DEPS_CHANGE、EVENT_PROJECT_PAGES_CHANGE、EVENT_PROJECT_BLOCKS_CHANGE、EVENT_PROJECT_APIS_CHANGE、EVENT_PROJECT_META_CHANGE、EVENT_PROJECT_PUBLISH、EVENT_PROJECT_FILE_PUBLISH、EVENT_PROJECT_GEN_SOURCE
    • 区块:EVENT_BLOCK_CHANGE
    • 节点:EVENT_NODE_CHANGE
    • 历史:EVENT_HISTORY_CHANGE、EVENT_HISTORY_LOAD

参考资料

VTJ.PRO 是一个开源的、AI 驱动的 Vue 3 企业级应用开发平台。它通过 AI 智能体与可视化编排实现高效开发,并支持导出标准 Vue 代码以避免平台锁定。更多信息请访问: