> **本文回答**：`RenderCache` 失效与 `forceFullRender()` —— 怎么强制重绘、什么时候会自动失效。

## 一、RenderCache 是什么

`RenderCache` 是**逐实体的 GeoJSON 缓存**（`Map<entityId, Feature[]>`）：`getOrCompute(entity)` 在 `entity.isDirty === false` 且命中缓存时直接返回上次的 Feature 数组，否则重跑 `entity.toGeoJSON()` + CAD→经纬度坐标变换并写回缓存。

效果：

- 未变化的实体跳过重编码——几万实体的图，只动一条时编码代价是 O(1) 而不是 O(N)
- 配合桶的增量摘除/重插，真正被重建的只有变化实体的那几个 feature

## 二、失效触发

要区分两件事：**RenderCache 失效**（逐实体）与**渲染调度**（按桶标脏→rAF 合并重绘）。

| 触发 | 效果 |
|---|---|
| `entity.changed()` | 该实体 `isDirty=true` → 下次 `getOrCompute` 重算 |
| `entity.styleChanged()` | 走 `updateStyleProps` 快路径，原地修缓存 feature 的样式属性 |
| `store.add / delete` | 增量更新：从所属桶摘除 / 重编码插入 |
| `renderPipeline.forceFullRender()` | `renderCache.invalidateAll()` 清全部缓存 + `_needFullRebuild` + `store.setDirty()` |
| `setMode`（browse/edit 切换） | 内部调 `forceFullRender` → 全量重建 |

**不会**让 RenderCache 失效的：`map.panBy / zoomTo / rotateTo`（缓存里存的是经纬度 feature，视图变换不影响）、`map.setStyle`（底座样式，与实体缓存无关）——它们只是让底座重绘。

正常业务场景**不用管**；框架自动处理。

## 三、手动强刷：`forceFullRender()`

某些边缘场景需要手动：

```js
mapcadLayer.renderPipeline.forceFullRender();   // 是方法不是属性
// 强制重绘一切，跳过所有缓存
```

典型场景：

- GPU context lost 后（比如手机切后台再切回）
- 外部改了 WebGL state（少见）
- 调试：对比渲染输出

## 四、标脏即重绘

没有公开的 `requestRender()`：实体 `changed()` / `styleChanged()` / 增删都会标脏，重绘由内部 `requestAnimationFrame` 自动合并到下一帧。一般不需要手动触发。

## 五、bus 事件 render:requested（⚠️ 1.0.1 未发射）

`VjmapextEvents.RenderRequested` 在枚举中有定义，但 1.0.1 bundle 内**没有任何 emit 点**——下面订阅永远收不到：

```js
bus.on(VjmapextEvents.RenderRequested, () => {   // ⚠️ 死代码
  console.log('request render');
});
```

实体标脏的观测出口用 `EntityAdded` / `EntityModified` / `EntityDeleted`；重绘本身由内部 `requestRender()` → `requestAnimationFrame` 驱动，无公开的重绘完成事件。

## 六、画面没更新？checklist

1. 实体属性改了但画面不变 → 调 `changed()` 或 `styleChanged()`。
2. 改了还是不变 → 确认实体没挂 `editable=false`/`visible=false`，也没被 `setHiddenLayerChecker` 隐藏。
3. 底座切换样式后图层不见 → `styledata` 事件里 re-addLayer。
4. resize 后地图模糊 → `map.resize()` 强制重算 canvas 尺寸。
5. 最后兜底 → `mapcadLayer.renderPipeline.forceFullRender()`。

## 七、GPU context lost

浏览器切后台 / 切标签 / 长时间休眠可能 lose context：

```js
map.getCanvas().addEventListener('webglcontextlost', e => {
  e.preventDefault();
  console.warn('GPU context lost');
});
map.getCanvas().addEventListener('webglcontextrestored', () => {
  console.log('GPU context restored');
  mapcadLayer.renderPipeline.forceFullRender();
});
```

底座和 FX 会自动恢复，但 vjmapext 实体缓存 + 自定义 plugin 可能需要手动重建。

## 八、热重载开发时

Vite HMR 后 mapcadLayer 已销毁，但 map 实例未销毁 → 画面残留：

```js
if (import.meta.hot) {
  import.meta.hot.dispose(() => {
    map.removeControl(mapcadLayer);
    map.remove();
  });
}
```

## 九、性能监测

```js
// ⚠️ 'render:requested' 1.0.1 从未发射；用实体标脏事件做脏帧率代理
//    （要盯底座渲染帧数可用 map.on('render', ...)）
let dirtyCount = 0, lastTs = performance.now();
for (const ev of ['entity:added', 'entity:modified', 'entity:deleted'])
  mapcadLayer.eventBus.on(ev, () => dirtyCount++);

setInterval(() => {
  const now = performance.now();
  console.log(`entity dirty events: ${dirtyCount} / ${((now - lastTs) / 1000).toFixed(1)}s`);
  dirtyCount = 0;
  lastTs = now;
}, 1000);
```

如果每秒脏事件 > 60 → 有反复标脏的源头（如 handler 里又改实体造成回环），排查。

## 十、常见坑

1. **每帧都调 forceFullRender()**：等价于禁用所有缓存，FPS 断崖。只在调试或极端场景调用。
2. **webglcontextlost 后不重建**：画面永黑；监听 restored。
3. **map.resize 忘调**：容器尺寸变化后画面模糊 / 对不齐；监听父元素 ResizeObserver。
4. **自定义 WebGL layer 没遵守 map 的 GL 状态**：破坏了缓存假设；在 `onRender` 里保存 / 恢复 state。

## 相关

- 渲染桶 → [render-buckets](render-buckets.md)
- 内存泄漏 → [memory-leaks](../../../vjmap/references/perf/memory-leaks.md)
- 调试 → [profiling](../../../vjmap/references/perf/profiling.md)
