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

Angular中Google Maps API偶现Uncaught ReferenceError: google未定义问题

解决Angular中继承google.maps.OverlayView时出现的"google is not defined"错误

这个问题的核心原因很明确:你用async defer加载Google Maps API的方式,和Angular组件类的解析执行时机冲突了。

async defer会让API脚本在页面DOM加载完成后才开始加载并执行,但Angular组件(包括你在组件内部定义的Popup类)是在模块编译、组件初始化阶段就被TypeScript解析的——这时候如果API还没加载完成,google命名空间自然不存在,就会抛出ReferenceError;偶尔能正常加载只是运气好,API刚好在类解析前加载完了而已。

下面给你两种靠谱的解决方案,适配Angular的开发场景:

方案一:用Promise封装API加载,在组件运行时动态定义Popup类

这种方式不需要修改太多现有代码,核心是把Popup类的定义延迟到API加载完成后执行:

  1. 修改index.html的API加载脚本,添加回调函数:
<script async defer src="https://maps.googleapis.com/maps/api/js?key=myKey&libraries=places&callback=googleMapsLoaded"></script>
  1. 创建一个全局的加载Promise(可以放在单独的文件比如google-maps-loader.ts里):
// 声明google全局变量,避免TypeScript报错
declare const google: any;

export const googleMapsLoaded$ = new Promise<void>((resolve) => {
  // 把回调挂到window上,让API加载完成后触发
  (window as any).googleMapsLoaded = () => {
    resolve();
  };
});
  1. 修改你的MapComponent,在ngAfterViewInit里等待API加载完成后再定义Popup类:
import { Component, AfterViewInit, OnDestroy } from '@angular/core';
import { googleMapsLoaded$ } from './google-maps-loader';

@Component({
  selector: 'app-map',
  templateUrl: './map.component.html',
  styleUrls: ['./map.component.scss'],
})
export class MapComponent implements AfterViewInit, OnDestroy {
  map: google.maps.Map;
  // 先声明Popup类型,后续动态赋值
  Popup: typeof google.maps.OverlayView;

  async ngAfterViewInit() {
    // 等待Google Maps API加载完成
    await googleMapsLoaded$;

    // 现在可以安全地定义Popup类了
    this.Popup = class Popup extends google.maps.OverlayView {
      // 你的类实现代码
      constructor(position: google.maps.LatLng, content: HTMLElement) {
        super();
        this.position = position;
        this.content = content;
        // ...其他初始化逻辑
      }

      // 实现OverlayView要求的方法:onAdd, draw, onRemove
      onAdd() { /* ... */ }
      draw() { /* ... */ }
      onRemove() { /* ... */ }
    };

    // 后续创建Popup实例时,用this.Popup
    // const popup = new this.Popup(myPosition, myContentElement);
  }

  // 其他地图相关代码...
}

方案二:用APP_INITIALIZER确保API在应用初始化前加载完成

如果希望整个应用启动前就确保Google Maps API加载完成,可以用Angular的APP_INITIALIZER钩子:

  1. 在app.module.ts中配置初始化器:
import { NgModule, APP_INITIALIZER } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import { MapComponent } from './map/map.component';

declare const google: any;

// 定义加载Google Maps的工厂函数
function loadGoogleMaps() {
  return new Promise<void>((resolve, reject) => {
    const script = document.createElement('script');
    script.src = 'https://maps.googleapis.com/maps/api/js?key=myKey&libraries=places';
    script.onload = () => resolve();
    script.onerror = () => reject(new Error('加载Google Maps API失败'));
    document.body.appendChild(script);
  });
}

@NgModule({
  declarations: [AppComponent, MapComponent],
  imports: [BrowserModule],
  providers: [
    {
      provide: APP_INITIALIZER,
      useFactory: loadGoogleMaps,
      multi: true
    }
  ],
  bootstrap: [AppComponent]
})
export class AppModule { }

这样Angular会在启动应用前先执行loadGoogleMaps函数,等API加载完成后才会初始化组件——这时候你在组件内部直接定义Popup类就不会再出现google is not defined的错误了。

额外注意事项

  • 确保你已经安装了Google Maps的TypeScript类型定义:npm install @types/googlemaps --save-dev,这样TypeScript不会报类型错误。
  • 避免在组件类的顶层直接引用google命名空间的类型(除非用方案二确保API提前加载),尽量把依赖google的代码放到运行时的生命周期钩子中。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.21 04:03:01