唯杰地图 logo唯杰地图
VJMAP
EN

listen

vjcad/references/event/listen.md 6.5 KB177 行
原文

本文回答:监听用 Engine.eventManager.on(CadEvents.X, handler);取消监听必须保留 handler 引用 off(...);一次性 once;批量场景用 suspendEvents() / resumeEvents() 提速;移除全部 offAll(event?)。注意 CadEvents 里约一半成员(DocumentOpened/Saving/Closing…、EntityErasing、CommandCancelled 等)当前版本没有派发点,订阅永不触发,清单见 overview 第三节。

一、API 一览

const em = Engine.eventManager;

em.on(CadEvents.EntityAdded, handler);       // 监听
em.once(CadEvents.DocumentCreated, handler); // 一次性
em.off(CadEvents.EntityAdded, handler);      // 取消
em.offAll(CadEvents.EntityAdded);            // 取消该事件全部
em.offAll();                                 // 取消所有事件全部

em.suspendEvents();    // 暂停(批量优化用)
em.resumeEvents();     // 恢复
em.isSuspended();      // 是否暂停

em.hasListeners(CadEvents.EntityAdded);
em.listenerCount(CadEvents.EntityAdded);

MCP 参考:vjcad/doc/event/event-usage.md。

方法 说明
on(event, handler) 监听事件
off(event, handler) 取消监听
once(event, handler) 一次性监听
offAll(event?) 移除全部监听
emit(event, args) 触发事件(内部使用)
suspendEvents() / resumeEvents() 暂停 / 恢复
isSuspended() 是否暂停
hasListeners(event) / listenerCount(event) 监听器查询

二、最小示例

import { Engine, CadEvents } from 'vjcad';

// 普通监听
Engine.eventManager.on(CadEvents.EntityAdded, (args) => {
  console.log(args.entity.type, args.entity.id);
});

// 一次性(DocumentOpened 当前版本不派发,别用它)
Engine.eventManager.once(CadEvents.DocumentCreated, (args) => {
  console.log('首个新文档:', args.document.name);
});

三、取消监听(保留 handler 引用)

const handler = (args) => { /* ... */ };

Engine.eventManager.on(CadEvents.EntityAdded, handler);
// ...
Engine.eventManager.off(CadEvents.EntityAdded, handler);

关键:off 必须传同一个 handler 引用,匿名函数 (args)=>{} 无法 off。

MCP 参考:vjcad/doc/event/event-usage.md。

四、批量操作时暂停事件(提速)

Engine.eventManager.suspendEvents();
try {
  for (let i = 0; i < 1000; i++) {
    const line = new LineEnt([i, 0], [i, 100]);
    line.setDefaults();
    Engine.pcanvas.addEntity(line);
  }
} finally {
  Engine.eventManager.resumeEvents();
}

注意:默认 suspendEvents()(queueEvents=false)暂停期间事件直接被丢弃;传 suspendEvents(true) 则排队暂存,resumeEvents() 时按序回放(suspendEvents(queueEvents?: boolean);2026-09-21 运行时验证:丢弃/回放两模式均成立,once 精确一次、offAll(event) 只清指定事件)。

MCP 参考:vjcad/doc/event/event-usage.md。

五、两种访问方式(等价)

// 方式一(推荐):通过 Engine
import { Engine, CadEvents } from 'vjcad';
Engine.eventManager.on(CadEvents.EntityAdded, handler);

// 方式二:直接 CadEventManager
import { CadEventManager, CadEvents } from 'vjcad';
const em = CadEventManager.getInstance();
em.on(CadEvents.EntityAdded, handler);

Engine.eventManager 内部就是 CadEventManager.getInstance()。

MCP 参考:vjcad/doc/event/event-usage.md。

六、可取消事件(-ing 前置)

// 阻止删除特定图层的实体(EntityErasing 不派发;删除只发批量版 EntitiesErasing)
Engine.eventManager.on(CadEvents.EntitiesErasing, (args) => {
  const protectedEnts = args.entities.filter(e => e.layer === '保护图层');
  if (protectedEnts.length) args.excludeEntities = protectedEnts;   // 部分排除;全拦用 args.cancel = true
});

// 阻止保存空文档(DocumentSaving 不派发,改在命令层拦截)
Engine.eventManager.on(CadEvents.CommandStarting, (args) => {
  if (['QSAVE', 'SAVEAS', 'SAVESERVER', 'SAVELOCAL'].includes(args.commandName)
      && Engine.getEntities(e => e.isAlive).length === 0) {
    args.cancel = true;
    alert('文档为空,无法保存');
  }
});

// 关闭前自定义校验(DocumentClosing 不派发;CLOSE 命令自带未保存确认框)
Engine.eventManager.on(CadEvents.CommandStarting, (args) => {
  if (args.commandName === 'CLOSE' && !myBusinessCheckPassed()) {
    args.cancel = true;
  }
});

⚠️ Engine.eraseEntities 在 EntitiesErasing 被取消/部分排除时仍按原数组记录撤销——重做会把受保护实体也删掉,详见 entity-events 第三节。

MCP 参考:vjcad/doc/event/event-usage.md(原文的 EntityErasing/DocumentSaving/DocumentClosing 示例在当前版本不触发,已改写)。

七、事件参数类型(TS)

import type {
  EntityAddedEventArgs,
  DocumentSwitchedEventArgs,
  CommandStartedEventArgs,
  EntitiesErasingEventArgs
} from 'vjcad';

Engine.eventManager.on(CadEvents.EntityAdded, (args: EntityAddedEventArgs) => {
  args.entity;
  args.timestamp;
});

Engine.eventManager.on(CadEvents.DocumentSwitched, (args: DocumentSwitchedEventArgs) => {
  args.currentDocument;
});

Engine.eventManager.on(CadEvents.CommandStarted, (args: CommandStartedEventArgs) => {
  args.commandName;
});

// 可取消事件用对应的 *ing 类型(已继承 CancellableEventArgs,带 cancel 字段)
Engine.eventManager.on(
  CadEvents.EntitiesErasing,
  (args: EntitiesErasingEventArgs) => {
    args.cancel = true;
  }
);

事件参数是逐事件类型(EntityAddedEventArgs/DocumentSwitchedEventArgs/…),全表见 EventArgsMap(未派发事件的 *EventArgs 类型也照样导出,类型存在不代表会触发);没有 EntityEventArgs/DocumentEventArgs/CommandEventArgs 这种泛型名。

MCP 参考:vjcad/doc/event/event-usage.md。

反例

  • on(...,(a)=>{}) + off(...,(a)=>{}):两个匿名函数引用不同,off 失败。
  • 在 EntityAdded 回调里 Engine.addEntities(...):会触发新一轮 EntityAdded,可能死循环。
  • 频繁 suspendEvents:影响其他依赖事件的功能(如 UI 状态栏 / 自动保存)。

参考(官方文档 MCP)

  • 文档:vjcad/doc/event/event-usage.md
  • 示例:vjcad/example/event/01entityadded.js