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

React Router单页应用如何避免重复加载Google Maps JS API?

如何在React Router单页应用中只加载一次Google Maps JS API?

我开发的是基于React Router的单页应用(SPA),其中一个页面用Google Maps JS API展示地图,采用官方加载方式:在DOM中添加带回调函数的Google Maps脚本,回调函数加载地图后存在组件状态里。

但遇到个棘手问题:用户在页面间导航时,React Router会卸载地图页面组件,导致每次返回地图页都得重新加载Google Maps API,谷歌会把这算成新的API调用次数,直接增加了计费成本!

有没有办法只加载一次Google Maps API,避免路由导航时反复卸载/重新加载?

附上地图页面的代码片段:

// 创建状态存储地图对象
const [map, setMap] = useState(null)

// Google Maps脚本加载完成后执行的回调函数
function initMap() {
  const map = new window.google.maps.Map(document.getElementById("map"), mapOptions)
  setMap(map)
  /* 其他操作比如:
  - 设置监听事件
  - 给地图添加标记
  */
}
window.initMap = initMap;

// 创建script标签添加到DOM
// 必须在initMap定义后再添加,所以不能直接写在index.html里
var script = document.createElement('script');
script.id="myscript"
script.src = 'https://maps.googleapis.com/maps/api/js?key=MY_API_KEY&callback=initMap';
script.async = true

useEffect(() => {
  const scriptElement = document.getElementById("myscript");
  document.head.appendChild(script);
},[])

// 组件卸载时删除DOM中的Google Maps脚本,并重置window.google为空对象,避免重复加载
useEffect(() => {
  return () => {
    let existingScript = document.getElementById("myscript")
    if (existingScript) {
      existingScript.remove()
    }
    window.google = {}
  }
}, [])

return (
  <Fragment>
    <div style={{ width: "100%", height: "100%" }} id="map"/> 
    {/* 其他JSX元素*/}
  </Fragment>
)

解决思路与方案

你的问题根源在于组件卸载时主动删除了API脚本并重置了window.google全局对象,导致每次进入页面都得重新加载API。可以从以下几个方向优化:

1. 检查API状态,避免重复注入脚本

修改脚本注入逻辑,先判断API是否已加载,只有未加载时才注入脚本,组件卸载时不再清理API相关资源:

useEffect(() => {
  // 先检查Google Maps API是否已加载完成
  if (window.google?.maps) {
    initMap();
    return;
  }

  // 检查DOM中是否已有对应的script标签
  let existingScript = document.getElementById("myscript");
  if (!existingScript) {
    const script = document.createElement('script');
    script.id="myscript"
    script.src = 'https://maps.googleapis.com/maps/api/js?key=MY_API_KEY&callback=initMap';
    script.async = true
    document.head.appendChild(script);
  } else {
    // 已有脚本但可能未触发回调,手动初始化地图
    initMap();
  }

  // 组件卸载时只清理地图实例相关资源,保留API脚本和全局对象
  return () => {
    if (map) {
      // 清理地图的监听事件、标记等,避免内存泄漏
      // 例如:移除所有标记、清空监听
    }
  }
}, [])

2. 把API加载逻辑提升到全局根组件

将Google Maps API的加载逻辑放在不会被卸载的根组件(比如App.js)中,确保整个应用生命周期内只加载一次API。之后地图页面直接使用已存在的window.google对象初始化地图即可,无需再处理脚本注入。

3. 你的最终方案:根组件创建地图对象并通过Context传递

正如你编辑中提到的,将地图对象的创建逻辑移到根组件,通过React Context传递给子组件,这是最推荐的方案:

  • 在根组件中完成API加载,或者直接初始化地图实例(如果地图容器在根组件)
  • 通过Context提供地图实例或API对象,地图页面直接从Context获取,完全复用已加载的API,彻底避免重复加载导致的额外计费

这种方式也符合谷歌的官方建议,能更好地管理地图实例的生命周期,同时避免不必要的API调用。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.22 04:00:44