listen
本文回答:监听用
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