batch-rendering
本文回答:批量新增 / 修改时的策略 —— vjmapext 侧渲染自动按帧合并;vjmap 底座侧一次
setData。
一、规则一句话
任何一个循环里有 >100 次实体 / source 修改 → 注意批处理。但 vjmapext 与 vjmap 底座的批处理机制完全不同,别套错。
二、vjmapext 实体:直接循环
for (const pt of data) {
mapcadLayer.addEntity(new vjmapext.PointEnt(pt));
}
addEntity 只标脏;重绘由 requestAnimationFrame 合并,循环 1 万次也只渲染一帧。没有 beginBulk/endBulk/bulkRun API。要把整批记为一次 undo:
mapcadLayer.undoManager.beginTransaction();
try {
for (const pt of data) mapcadLayer.addEntity(new vjmapext.PointEnt(pt));
} finally {
mapcadLayer.undoManager.endTransaction();
}
三、vjmap 底座 source:一次 setData
// ❌ 错:10 次 setData
const fc = { type: 'FeatureCollection', features: [] };
for (const f of newFeatures) {
fc.features.push(f);
src.setData(fc); // 每次都触发重绘
}
// ✅ 对:先凑齐再 setData
const fc2 = { type: 'FeatureCollection', features: [...oldFeatures, ...newFeatures] };
src.setData(fc2);
四、feature-state(只改样式)
// 批量换色:不 setData,直接 feature-state
for (const f of features) {
map.setFeatureState({ source: 'src', id: f.id }, { highlighted: true });
}
// layer 的 paint 用表达式读 state
map.addLineLayer('lyr', 'src', {
lineColor: ['case',
['boolean', ['feature-state', 'highlighted'], false], '#f00',
'#888'
]
});
1 万 feature 换色只要几十 ms。
五、FX 批量:addPoints
// 两种写法等效(推荐后者,代码更简洁、直接拿 handle 数组)
for (const p of list) fx.addPoint('fire', { cad: p, size: 30 });
fx.addPoints('fire', list.map(p => ({ cad: p, size: 30 })));
1.0.1 的 addPoints 内部就是循环 addPoint,两者都只写 CPU 影子缓冲、在下一帧 draw 时合并成一次 GPU 上传——没有数量级的速度差。
六、多批混合添加
for (const e of batch1) mapcadLayer.addEntity(e);
for (const e of batch2) mapcadLayer.addEntity(e);
// 同步循环内全部标脏,下一帧一次 rebuild
七、styleChanged 批量
改几百个实体颜色:
for (const e of selected) {
e.color = 0xff0000;
e.styleChanged();
}
配合 styleChanged(见 style-only-fast-path)更快。
八、Web Worker 预计算
大批量数据解析 / 转换应放 Worker:
// worker.js
self.onmessage = e => {
const list = parseCSV(e.data); // 解析 CSV 大字符串
const features = list.map(...);
self.postMessage({ features }, [features]); // transfer
};
// main
const w = new Worker(new URL('./worker.js', import.meta.url));
w.postMessage(csv);
w.onmessage = ev => {
src.setData({ type: 'FeatureCollection', features: ev.data.features });
};
九、流式加载(分批 setData)
百万点分 10 批:
const total = bigList;
const BATCH = 100_000;
let cursor = 0;
const fc = { type: 'FeatureCollection', features: [] };
function nextBatch() {
const end = Math.min(cursor + BATCH, total.length);
for (let i = cursor; i < end; i++) fc.features.push(total[i]);
src.setData(fc);
cursor = end;
if (cursor < total.length) requestAnimationFrame(nextBatch);
}
nextBatch();
用户看到数据"流入"而不是一卡一卡。
十、删除:批量
for (const id of ids) mapcadLayer.deleteEntity(id);
十一、事务包装
mapcadLayer.undoManager.beginTransaction();
try {
for (const e of entities) mapcadLayer.addEntity(e);
} finally {
mapcadLayer.undoManager.endTransaction();
}
// 一次 Undo 撤回整批
十二、常见坑
- 循环里穿插 await:把添加摊到多个帧;同步循环一把加完。
- undoManager 事务忘 endTransaction:后续 undo 记录全挂在未闭合事务里;必须 try/finally。
- setData 频繁调:每次都全量重排索引;必须合并。
- 循环里每条都
entity.dynamic = true:dynamic 桶被撑爆;静态数据用 cold。 - addPoints 传错结构:opts 对象里必须有
cad / lngLat / mercator其中一个。
相关
- 实体批量 → batch/entity-bulk-add
- GeoJSON 批量 → batch/geojson-bulk
- 样式批量 → batch/bulk-style-update
- FX 批量 → fx-quality