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
相关产品推荐
相关产品推荐

