树结构无感更新及地图大批量点位上图Ui卡顿优化
一、树结构无感更新
在实际的项目应用中,有时候需要定时更新树结构。但是要避免影响用户的使用,这时候如何做到无感更新呢?
1、此处以统计数量的树结构为例,无感更新数量
高性能版本:一次建表,快速更新
具体的实现代码如下所示
/**
* 构建节点 Map,id -> 节点引用
*/
function buildNodeMap(tree, map = new Map()) {
for (const node of tree) {
map.set(node.id, node);
if (node.children) {
buildNodeMap(node.children, map);
}
}
return map;
}
/**
* 用新树的 count 更新旧树
*/
function updateCountsByMap(oldTreeMap, newTree) {
for (const node of newTree) {
const oldNode = oldTreeMap.get(node.id);
if (oldNode) {
oldNode.count = node.count; // 更新数量
if (node.children) {
updateCountsByMap(oldTreeMap, node.children); // 递归同步
}
}
}
}
// ======================
// 示例
// ======================
// 旧树(带 checked)
let oldTree = [
{ id: 1, name: 'A', count: 5, checked: true, children: [
{ id: 2, name: 'A1', count: 2, checked: false },
{ id: 3, name: 'A2', count: 4, checked: true }
] }
];
// 新树(最新 count)
let newTree = [
{ id: 1, name: 'A', count: 7, children: [
{ id: 2, name: 'A1', count: 3 },
{ id: 3, name: 'A2', count: 6 }
] }
];
// 先构建旧树的 Map(只做一次)
const oldTreeMap = buildNodeMap(oldTree);
// 定时更新
setInterval(() => {
// 模拟接口返回
const latestTree = JSON.parse(JSON.stringify(newTree));
// 快速更新旧树的数量
updateCountsByMap(oldTreeMap, latestTree);
console.log("旧树已更新:", JSON.stringify(oldTree, null, 2));
}, 5000);
2、此更新优点
1、O(n) 更新速度
因为每个节点查找是 O(1)(Map),所以总时间复杂度是节点数量的线性。2、大树高频更新不卡
即使 10 万节点,每次更新也能在几十毫秒内完成。3、引用不变
旧树引用保持不变(适合 Vue / React),不会引起整棵树重渲染,只会局部响应。4、保留所有状态
checked、expanded 等 UI 状态不受影响。
二、地图大批量点位上图UI卡顿优化
1、涉及大量计算导致UI卡顿
使用Worker 多线程解析数据,这样可以减少主线程的压力。避免造成UI卡顿,用户体验不好
2、点位数量大导致UI卡顿
解决思路主要有以下几点:
视野内渲染(Spatial Filter)
大多数时候用户只看得到当前地图窗口里的点,视野外的点可以不画聚合(ClusterLayer
work + 分帧渲染
work + 分帧渲染 简单实例代码如下:
const worker = new Worker('worker.js');
const layer = new maptalks.VectorLayer('layer').addTo(map);
worker.onmessage = e => {
const points = e.data;
const batchSize = 500; // 每帧加载数量
let index = 0;
function addBatch() {
const batch = points.slice(index, index + batchSize)
.map(p => new maptalks.Marker([p.lng, p.lat], {
symbol: {
markerType: 'ellipse',
markerFill: p.color,
markerWidth: 8,
markerHeight: 8
}
}));
layer.addGeometry(batch);
index += batchSize;
if (index < points.length) {
requestAnimationFrame(addBatch);
}
}
requestAnimationFrame(addBatch);
};
// 启动
worker.postMessage({ points: allPoints });
🔹 为什么这样能避免卡顿
数据准备在 Worker → 主线程完全不卡
渲染分批 → 每一帧加少量点,保证渲染时间 < 16ms,不阻塞 UI
体验效果 → 用户可以先看到部分点,地图可以流畅缩放/拖拽,剩余点在后台慢慢加