如何优化TypeScript中Google地图filterMarkers内的.forEach长任务
优化Google地图标记筛选性能:按分组提前缓存标记
核心优化方案:提前按agrupacion分组缓存标记
你提到的每次筛选都遍历所有标记的问题,可以通过初始化阶段按agrupacion字段预分组标记来解决,这样筛选时只需操作目标分组,避免全量遍历。
步骤1:新增分组缓存变量
在工具类中添加一个私有Map用于存储不同分组的标记数组:
export class MapTool { private map!: google.maps.Map; private markers: google.maps.Marker[] = []; private markerClusterer?: MarkerClusterer; // 新增:按agrupacion分组的标记缓存 private markerGroups: Map<string, google.maps.Marker[]> = new Map(); private localizaciones: Localizacion[] = []; // 假设这是你的数据类型 // ... 其他方法 }
步骤2:初始化时填充分组缓存
在initMap方法中,创建每个标记后,将其加入对应的分组:
initMap() { this.map = new google.maps.Map(document.getElementById('map')!, { // 地图配置(中心点、缩放等级等) }); this.localizaciones.forEach((loc, index) => { const marker = new google.maps.Marker({ position: new google.maps.LatLng(loc.lat, loc.lng), map: this.map, // 其他标记配置(图标、标题等) }); // 将标记加入全局数组(如果仍需要全量操作的话) this.markers.push(marker); // 填充分组缓存 const agrupacion = loc.agrupacion; if (!this.markerGroups.has(agrupacion)) { this.markerGroups.set(agrupacion, []); } this.markerGroups.get(agrupacion)?.push(marker); }); // 初始化聚合器,开启忽略隐藏标记选项 this.markerClusterer = new MarkerClusterer({ map: this.map, markers: this.markers, ignoreHidden: true, // 减少聚合计算时的无效遍历 }); }
步骤3:修改filterMarkers函数,利用分组缓存
现在筛选时无需遍历所有标记,直接操作目标分组:
filterMarkers(filtro: string) { // 逻辑1:显示所有标记 if (filtro === 'todos') { this.markerGroups.forEach(group => { group.forEach(marker => marker.setVisible(true)); }); } // 逻辑2:仅显示指定分组的标记 else { // 先隐藏所有标记(可根据实际筛选逻辑调整) this.markerGroups.forEach(group => { group.forEach(marker => marker.setVisible(false)); }); // 显示目标分组 const targetGroup = this.markerGroups.get(filtro); targetGroup?.forEach(marker => marker.setVisible(true)); } // 更新聚合器视图 this.markerClusterer?.repaint(); }
如果你的筛选支持多选多个agrupacion,只需遍历选中的分组列表即可,进一步减少操作量。
其他性能优化建议
防抖筛选触发事件:如果筛选是由输入框、下拉框等高频触发的事件驱动,添加防抖逻辑避免频繁执行筛选:
// 工具类中新增防抖方法 private debouncedFilter = (() => { let timeoutId: NodeJS.Timeout; return (filtro: string) => { clearTimeout(timeoutId); timeoutId = setTimeout(() => { this.filterMarkers(filtro); }, 300); // 300ms防抖间隔,可根据需求调整 }; })(); // 调用时使用debouncedFilter代替直接调用filterMarkers // 例如在输入框事件中: // inputElement.addEventListener('input', (e) => { // this.debouncedFilter((e.target as HTMLInputElement).value); // });分批创建标记:如果标记数量极大(数千个以上),使用
requestIdleCallback分批创建标记,避免阻塞主线程:initMap() { this.map = new google.maps.Map(document.getElementById('map')!, { // 地图配置 }); let currentIndex = 0; const batchSize = 20; // 每批创建20个标记 const createBatch = () => { const endIndex = Math.min(currentIndex + batchSize, this.localizaciones.length); for (; currentIndex < endIndex; currentIndex++) { const loc = this.localizaciones[currentIndex]; const marker = new google.maps.Marker({ position: new google.maps.LatLng(loc.lat, loc.lng), map: this.map, }); this.markers.push(marker); // 填充分组缓存 const agrupacion = loc.agrupacion; if (!this.markerGroups.has(agrupacion)) { this.markerGroups.set(agrupacion, []); } this.markerGroups.get(agrupacion)?.push(marker); } if (currentIndex < this.localizaciones.length) { requestIdleCallback(createBatch); } else { // 所有标记创建完成后初始化聚合器 this.markerClusterer = new MarkerClusterer({ map: this.map, markers: this.markers, ignoreHidden: true, }); } }; requestIdleCallback(createBatch); }复用信息窗口:如果标记点击会弹出信息窗口,不要每次点击都创建新的
InfoWindow实例,提前创建一个全局实例,点击时更新内容即可:private infoWindow = new google.maps.InfoWindow(); // 在创建标记时绑定点击事件 marker.addListener('click', () => { this.infoWindow.setContent(`<div>${loc.nombre}</div>`); this.infoWindow.open(this.map, marker); });
内容的提问来源于stack exchange,提问作者tsiPlus
相关产品推荐
相关产品推荐

