You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Angular结合AGM首次加载地图渲染耗时过长问题咨询

解决Angular 5 + AGM渲染大量标记点页面冻结的问题

嗨,我之前在项目里用AGM渲染几百个标记点时也踩过这个坑,页面直接卡半分钟才反应过来,太难受了!下面几个优化方案亲测有效,你可以挨个试试:

1. 优先用标记聚类(最省心的方案)

AGM自带了agm-marker-cluster组件,能把近距离的标记自动聚合成一个集群标记,点击集群才会展开显示具体的点。这样原本420个标记可能会变成十几个集群,渲染压力瞬间下降,页面基本不会卡了。

用法很简单,把原来的agm-marker都包裹在agm-marker-cluster里就行:

<agm-map [latitude]="lat" [longitude]="lng">
  <agm-marker-cluster>
    <agm-marker 
      *ngFor="let marker of markers"
      [latitude]="marker.lat"
      [longitude]="marker.lng">
    </agm-marker>
  </agm-marker-cluster>
</agm-map>

你还可以自定义集群的样式、图标,这个方案见效最快,推荐先试这个。

2. 给标记组件启用OnPush变更检测

每个agm-marker都是一个Angular组件,默认的变更检测策略会频繁检查所有组件状态,420个组件的话会给Angular带来极大压力。把标记组件的变更检测改成OnPush,只有当输入属性变化时才触发检测,能减少很多不必要的计算。

如果是你自己封装的标记组件,直接在@Component里配置:

import { ChangeDetectionStrategy, Component } from '@angular/core';

@Component({
  selector: 'app-custom-marker',
  templateUrl: './custom-marker.component.html',
  changeDetection: ChangeDetectionStrategy.OnPush // 启用OnPush
})
export class CustomMarkerComponent {
  @Input() markerData: any;
}

如果用的是AGM原生的agm-marker,可以考虑自己封装一层,把输入属性传递进去,然后给封装的组件加OnPush。

3. 用Google Maps原生API创建标记

AGM的组件封装虽然方便,但每个组件都有Angular的生命周期和变更检测开销。直接用Google Maps原生API创建标记,绕过Angular的组件树,性能会提升很多。

步骤大概是这样:

  1. agm-mapmapReady事件里拿到原生地图实例:
<agm-map [latitude]="lat" [longitude]="lng" (mapReady)="onMapReady($event)"></agm-map>
  1. 在组件里保存地图实例,然后循环创建原生标记:
private map: google.maps.Map;

onMapReady(map: google.maps.Map) {
  this.map = map;
  this.renderMarkers();
}

renderMarkers() {
  // 先清除旧标记(如果需要)
  // 然后遍历你的数据源创建标记
  this.markers.forEach(markerData => {
    new google.maps.Marker({
      position: { lat: markerData.lat, lng: markerData.lng },
      map: this.map,
      // 其他属性比如标题、图标等
    });
  });
}

这种方式完全脱离Angular的组件体系,渲染速度会快很多,唯一的缺点是需要自己管理标记的更新、删除等操作,但对于大量标记来说,这点代价很值得。

4. 只渲染当前视野内的标记

如果不想用聚类,还可以监听地图的视野变化,只渲染当前视口范围内的标记点。比如维护一个visibleMarkers数组,当地图视野改变时,计算哪些标记在当前 bounds 内,更新这个数组,然后*ngFor只遍历visibleMarkers

实现思路:

  • 监听地图的boundsChange事件(AGM里是boundsChange
  • 拿到当前地图的 bounds,遍历所有标记,判断是否在 bounds 内
  • 更新visibleMarkers数组,Angular会自动渲染这些标记

这样每次只渲染几十上百个标记,页面就不会卡了。


按照我的经验,先试标记聚类,基本上能解决大部分性能问题;如果聚类不符合你的需求,再试试原生API或者视野过滤的方案。

内容的提问来源于stack exchange,提问作者Moayad Ahmad

火山引擎 最新活动