batch-lag
本文回答:批量
addEntity/setData卡顿 —— 没合并 dirty / 没 bulk。
一、症状
- 循环 addEntity 1 万条 → 页面卡死 3~5 秒
- 循环改 GeoJSON feature → FPS 跌到 5
- 改颜色改透明度 → 每次都有感明显卡
二、根因:每条操作都触发一次重渲染
for (const d of data) {
mapcadLayer.addEntity(new vjmapext.PointEnt(d));
// 每次 addEntity:标脏 + 写空间索引 + 发 entity:added 事件
}
注意 RAF 合并保证渲染只发生一次——1 万次循环的真实开销在 JS 侧(实体构造、toGeoJSON 编码、空间索引插入),不是 1 万次渲染。卡的根因是单次循环量大,不是"没合并渲染"。
三、解法:直接循环(无 bulk API,RAF 自动合并)
// ✅ 直接循环即可——addEntity 只标脏,重绘按 requestAnimationFrame 合并到下一帧
for (const d of data) {
mapcadLayer.addEntity(new vjmapext.PointEnt(d));
}
要点:循环内不要穿插 await(会把添加摊到多帧);要一步撤销可包 undoManager.beginTransaction()/endTransaction()。
四、GeoJSON 批量
// ❌
for (const f of newFeatures) {
fc.features.push(f);
src.setData(fc); // 每次重排索引
}
// ✅
fc.features.push(...newFeatures);
src.setData(fc); // 一次
五、大量样式变化(只改颜色)
// ❌ 慢:changed() 全量
for (const e of ents) {
e.color = 0xff0000;
e.changed();
}
// ✅ 快:styleChanged()
for (const e of ents) {
e.color = 0xff0000;
e.styleChanged();
}
六、FX 批量
// 两者性能相当:都只写 CPU 影子缓冲,下一帧合并成一次 GPU 上传
for (const p of points) fx.addPoint('fire', { cad: p, size: 30 });
fx.addPoints('fire', points.map(p => ({ cad: p, size: 30 }))); // 写法更简洁
FX 卡顿多半不是"逐个 add"造成的,而是实例数/预设档位过高:看 fx.getMetrics() 的 live 数,降 fx.setQuality('lite'),海量同形点改 fx.symbolImage。
七、流式加载(不阻塞 UI)
1000 条还 OK,10 万条 bulk 也可能让 UI 卡 1~2 秒。分批:
const BATCH = 500;
let idx = 0;
function loadBatch() {
const end = Math.min(idx + BATCH, data.length);
for (; idx < end; idx++) {
mapcadLayer.addEntity(toEnt(data[idx]));
}
if (idx < data.length) requestAnimationFrame(loadBatch);
}
loadBatch();
每帧 500 条,用户看到数据"流入"而非一卡。
八、Web Worker 预处理
大批量 CSV/JSON 解析放 Worker:
// worker.js
self.onmessage = e => {
const text = e.data;
const ents = parseCSV(text).map(r => ({ type: 'Point', coords: [r.x, r.y] }));
self.postMessage(ents);
};
// main
const w = new Worker('worker.js');
w.postMessage(csvText);
w.onmessage = e => {
for (const d of e.data) mapcadLayer.addEntity(toEnt(d));
};
九、诊断流程
- F12 → Performance → 录制 bulk 加载过程
- 看主线程火焰图里最大的块
- 如果是
rebuild bucket/buffer upload多次 → 检查是不是每帧都在改实体 - 如果是
encode / setData多次 → 没合并 - 如果是
parseJSON / forEach→ 考虑 Worker
十、和 dynamic 的关系
批量添加和 dynamic 解决不同问题:
- 一次加载 1 万静态实体 → 直接循环 addEntity(RAF 合并渲染)+ 不 dynamic
- 持续改 100 条位置 → dynamic
十一、常见坑
- 循环里穿插 await:把添加摊到多帧、拉长总耗时;同步循环一把加完。
- 循环 await 大量异步:每条都 await 网络 → 串行非常慢;用
Promise.all并发。 - GeoJSON source 频繁 setData:source.setData 每次重排索引;先合并成完整 FeatureCollection 再设。
- 改了 100 条几何 + 100 条只改色:分开处理,几何用
changed(),样式用styleChanged()。
相关
- 渲染桶 → perf/render-buckets
- 批量策略 → perf/batch-rendering
- 实体批量 → batch/entity-bulk-add
- GeoJSON 批量 → batch/geojson-bulk