> **本文回答**：渲染 / 存储事件 —— `render:requested` / `store:changed`（⚠️ 1.0.1 枚举有定义但**从未发射**）+ 真实可用的 `undo:performed` / `redo:performed`。

## 一、事件列表

| 常量 | 字符串 | payload | 状态 |
|---|---|---|---|
| `RenderRequested` | `render:requested` | — | ⚠️ 1.0.1 bundle 中**无任何 emit 点**（订阅永远收不到） |
| `StoreChanged` | `store:changed` | — | ⚠️ 同上，死枚举 |
| —（非枚举） | `undo:performed` | 无 payload | ✅ `undoManager.undo()` 完成后发射 |
| —（非枚举） | `redo:performed` | 无 payload | ✅ `undoManager.redo()` 完成后发射 |

"变更感知"请改用确实会 fire 的事件：`EntityAdded` / `EntityModified` / `EntityDeleted` / `SelectionChanged` / `CommandEnded`（见 [bus-entity](bus-entity.md)）。

## 二、entity:added

```js
const bus = mapcadLayer.eventBus;
bus.on(VjmapextEvents.EntityAdded, ({ entity }) => {
  console.log('新增实体', entity.id, entity.type);   // 每次 addEntity 都 fire
});
```

`EntityAdded` payload 形如 `{ entity }`（entity 带 `id` / `type` / `color` / `layerId` 等）。同族还有 `EntityDeleted` / `EntityModified`（见 [bus-entity](bus-entity.md)）。

## 三、自动保存 / Dirty 标记

```js
let dirty = false;
bus.on(VjmapextEvents.EntityAdded, () => { dirty = true; });
bus.on(VjmapextEvents.EntityModified, () => { dirty = true; });
bus.on(VjmapextEvents.EntityDeleted, () => { dirty = true; });

setInterval(async () => {
  if (!dirty) return;
  dirty = false;
  await saveToServer(mapcadLayer.toJSON());
}, 5000);
```

## 四、undo 按钮状态

`undo:changed` 事件在 1.0.1 不存在；真实发射的是 `undo:performed` / `redo:performed`（`UndoManager` 直发，不在 `VjmapextEvents` 枚举内，用字面量订阅）。同步工具栏启用态：

```js
function refreshUndoBtns() {
  myUndoBtn.disabled = !mapcadLayer.undoManager.canUndo;   // 你自己的 UI 按钮
  myRedoBtn.disabled = !mapcadLayer.undoManager.canRedo;
}
bus.on('undo:performed', refreshUndoBtns);
bus.on('redo:performed', refreshUndoBtns);
bus.on(VjmapextEvents.CommandEnded, refreshUndoBtns);
```

栈深无公共 API；用 `undoManager.canUndo` / `canRedo` 判断可用性（内部 `undoStack` 为私有字段，勿依赖）。

## 五、render:requested

1.0.1 中该事件**不会被发射**（枚举成员存在但 bundle 内无 emit 点），下面代码永远收不到回调：

```js
bus.on(VjmapextEvents.RenderRequested, () => {
  // ⚠️ 死代码——事件定义存在但从未 fire
});
```

重绘由 `EntityStore.requestRender()` 内部调度驱动（标脏即入帧）。需要主动整帧重绘时可调公有方法 `mapcadLayer.renderPipeline.forceFullRender()`（`renderPipeline` 是 `MapCadLayer` 的公有 `readonly` 字段，内置 JsonPanel/ScriptRunner 都这么用）；但仍**没有"重绘完成"回调事件**，`render:requested` 也不发射。

## 六、常见坑

1. **`store:changed` 永远收不到**：1.0.1 未发射——若老代码订阅了它做 dirty 标记会失效，改挂 `entity:*`。
2. **自动保存频率太高**：实体事件每次 add 都 fire；加 debounce。
3. **UI 不刷新**：可能监听了错事件；组件应监听 `entity:*` / `selection:changed` / `command:ended`。
4. **undo 栈 UI 不更新**：1.0.1 没有 `undo:changed`；用 `undo:performed` / `redo:performed` / `command:ended` 刷新按钮态（`store:changed` 同样是死枚举，不要依赖）。

## 相关

- 实体事件 → [bus-entity](bus-entity.md)
- 选集事件 → [bus-selection](bus-selection.md)
- 批量 → [batch/entity-bulk-add](../batch/entity-bulk-add.md)
- Undo → [drawing/undo-redo](../drawing/undo-redo.md)
