You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何优化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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.06 12:56:28