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

Angular懒加载Google Maps API重复加载报错求助

解决Angular Google Maps API重复加载问题

一、核心问题分析

即便使用根注入的单例服务,若加载逻辑未检查API是否已加载/正在加载,可能因异步操作的竞态条件(比如多个组件同时触发加载请求)导致重复发起API请求;另外,@angular/google-maps本身内置了API加载机制,若自定义加载逻辑和它冲突,也会触发重复加载报错。

二、正确实现方式

1. 完善单例MapService的加载逻辑

在服务中维护加载状态,确保只发起一次API请求,同时处理已加载、正在加载的场景:

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable, of } from 'rxjs';

@Injectable({ providedIn: 'root' })
export class MapService {
  private apiLoaded = false;
  private loadingPromise: Promise<void> | null = null;
  private readonly apiKey = '你的Google Maps API密钥';
  private readonly apiUrl = `https://maps.googleapis.com/maps/api/js?key=${this.apiKey}&callback=initMap`;

  constructor(private http: HttpClient) {}

  loadGoogleMapsApi(): Observable<boolean> {
    // 已加载直接返回成功状态
    if (this.apiLoaded) {
      return of(true);
    }

    // 正在加载中,复用同一个Promise避免重复请求
    if (this.loadingPromise) {
      return new Observable(observer => {
        this.loadingPromise?.then(() => {
          observer.next(true);
          observer.complete();
        });
      });
    }

    // 首次发起加载请求
    this.loadingPromise = new Promise((resolve, reject) => {
      // 全局声明JSONP所需的回调函数
      (window as any).initMap = () => {
        this.apiLoaded = true;
        resolve();
      };

      this.http.jsonp(this.apiUrl, 'callback').subscribe({
        next: () => {},
        error: (err) => {
          this.loadingPromise = null;
          reject(err);
        }
      });
    });

    return new Observable(observer => {
      this.loadingPromise?.then(() => {
        observer.next(true);
        observer.complete();
      }).catch(err => {
        observer.error(err);
      });
    });
  }
}

2. 共享地图组件中调用服务

在组件的ngOnInit(而非构造函数)中调用服务的加载方法,确保组件初始化时触发加载,且仅等待一次加载完成:

import { Component, OnInit } from '@angular/core';
import { MapService } from './map.service';

@Component({
  selector: 'app-shared-map',
  templateUrl: './shared-map.component.html',
  styleUrls: ['./shared-map.component.css']
})
export class SharedMapComponent implements OnInit {
  mapLoaded = false;

  constructor(private mapService: MapService) {}

  ngOnInit(): void {
    this.mapService.loadGoogleMapsApi().subscribe({
      next: () => {
        this.mapLoaded = true;
        // 此处编写地图初始化逻辑
      },
      error: (err) => {
        console.error('Google Maps API加载失败:', err);
      }
    });
  }
}

3. 利用@angular/google-maps内置配置(更简洁方案)

@angular/google-maps的GoogleMapsModule提供了forRoot配置项,框架会自动处理单例加载,无需手动编写JSONP请求:

// app.module.ts
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { GoogleMapsModule } from '@angular/google-maps';

import { AppComponent } from './app.component';

@NgModule({
  declarations: [AppComponent],
  imports: [
    BrowserModule,
    GoogleMapsModule.forRoot({
      apiKey: '你的Google Maps API密钥',
      // 可选:添加其他API参数,如语言、扩展库等
      libraries: ['places']
    })
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule {}

这种方式下,直接在共享组件中使用<google-map>组件即可,无需手动处理加载逻辑:

<!-- shared-map.component.html -->
<google-map *ngIf="true" [center]="{lat: 40.7128, lng: -74.0060}" [zoom]="12"></google-map>

三、额外注意事项

  • 确保项目中没有在index.html等位置手动引入Google Maps API脚本,否则会和代码中的加载逻辑冲突。
  • 仅在根模块使用GoogleMapsModule.forRoot(),其他模块只需导入GoogleMapsModule(不带forRoot)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.13 08:10:17