Ленивая загрузка Google Maps JavaScript API основана на принципе отложенного подключения тяжёлого внешнего скрипта и инициализации карты только в момент, когда она действительно становится необходимой. Такой подход снижает время первоначальной загрузки страницы, уменьшает объём блокирующих ресурсов и улучшает показатели производительности, включая Core Web Vitals.
Google Maps JavaScript API является одним из наиболее ресурсоёмких внешних SDK: он загружает десятки модулей, шрифты, изображения тайлов, сервисы геокодирования и рендеринга. Если подключать его синхронно при старте страницы, это приводит к увеличению First Contentful Paint и задержке интерактивности.
Ключевая идея заключается в том, чтобы не подключать скрипт
API в <head> статически, а создавать его
динамически:
function loadGoogleMapsApi(apiKey) {
return new Promise((resolve, reject) => {
if (window.google && window.google.maps) {
resolve(window.google.maps);
return;
}
const script = document.createElement('script');
script.src = `https://maps.googleapis.com/maps/api/js?key=${apiKey}&callback=initMap`;
script.async = true;
script.defer = true;
window.initMap = () => resolve(window.google.maps);
script.oner ror = reject;
document.head.appendChild(script);
});
}
Такой подход обеспечивает:
После загрузки API карта создаётся стандартным образом, но строго внутри callback-логики:
function createMap() {
const map = new google.maps.Map(document.getElementById("map"), {
center: { lat: 49.8064, lng: 73.0855 },
zoom: 12,
});
new google.maps.Marker({
position: { lat: 49.8064, lng: 73.0855 },
map,
});
}
Связка с ленивой загрузкой:
loadGoogleMapsApi("API_KEY").then(() => {
createMap();
});
Наиболее эффективная стратегия — загрузка карты только тогда, когда контейнер попадает в область видимости.
const mapContainer = document.getElementById("map");
const observer = new IntersectionObserver((entries) => {
if (entries[0].isIntersecting) {
observer.disconnect();
loadGoogleMapsApi("API_KEY").then(() => {
createMap();
});
}
});
observer.observe(mapContainer);
Такой механизм предотвращает загрузку API, если пользователь никогда не прокручивает страницу до карты.
До момента загрузки API контейнер карты обычно заменяется статическим содержимым:
<div id="map">
<div class="map-placeholder">
Загрузка карты...
</div>
</div>
После инициализации placeholder удаляется:
function createMap() {
const container = document.getElementById("map");
container.innerHTML = "";
const map = new google.maps.Map(container, {
center: { lat: 49.8064, lng: 73.0855 },
zoom: 12,
});
return map;
}
Повторное подключение API недопустимо. Поэтому используется глобальный кеш промиса:
let googleMapsPromise = null;
function getGoogleMaps(apiKey) {
if (googleMapsPromise) return googleMapsPromise;
googleMapsPromise = new Promise((resolve, reject) => {
const script = document.createElement("script");
script.src = `https://maps.googleapis.com/maps/api/js?key=${apiKey}&callback=__gmapsInit`;
script.async = true;
script.defer = true;
window.__gmapsInit = () => resolve(window.google.maps);
script.oner ror = reject;
document.head.appendChild(script);
});
return googleMapsPromise;
}
Это исключает:
<script>;google.maps.В SPA архитектуре карта часто монтируется динамически при переходе на маршрут. В этом случае важно:
let mapInstance = null;
function mountMap() {
getGoogleMaps("API_KEY").then(() => {
mapInstance = new google.maps.Map(document.getElementById("map"), {
center: { lat: 49.8064, lng: 73.0855 },
zoom: 12,
});
});
}
function unmountMap() {
if (!mapInstance) return;
mapInstance = null;
document.getElementById("map").innerHTML = "";
}
Google Maps API поддерживает подзагрузку библиотек через параметр
libraries. При ленивой загрузке важно избегать избыточного
набора:
script.src =
"https://maps.googleapis.com/maps/api/js?key=API_KEY&libraries=places,geometry";
Оптимальная стратегия:
places, geometry,
drawing только при необходимости;Проблема возникает при одновременном вызове инициализации из разных компонентов.
Решение — единый менеджер загрузки:
class GoogleMapsLoader {
static promise = null;
static load(apiKey) {
if (this.promise) return this.promise;
this.promise = new Promise((resolve, reject) => {
const script = document.createElement("script");
script.src = `https://maps.googleapis.com/maps/api/js?key=${apiKey}&callback=__initGMaps`;
script.async = true;
script.defer = true;
window.__initGMaps = () => resolve(window.google.maps);
script.oner ror = reject;
document.head.appendChild(script);
});
return this.promise;
}
}
При нестабильном соединении ленивую загрузку дополняют:
function loadWithTimeout(ms) {
return Promise.race([
getGoogleMaps("API_KEY"),
new Promise((_, reject) =>
setTimeout(() => reject("timeout"), ms)
),
]);
}
При наличии нескольких контейнеров карта не должна грузиться повторно. Вместо этого используется общий API и отдельные экземпляры:
loadGoogleMapsApi("API_KEY").then(() => {
document.querySelectorAll(".map").forEach((el) => {
new google.maps.Map(el, {
center: { lat: 49.8, lng: 73.1 },
zoom: 10,
});
});
});
Помимо прокрутки применяются дополнительные триггеры:
button.addEventListener("click", () => {
loadGoogleMapsApi("API_KEY").then(createMap);
});
Частые проблемы при ленивой загрузке:
<script> без
кеширования;new google.maps.Map до загрузки API;window.initMap;window.google.maps.Корректная архитектура всегда строится вокруг единственного источника загрузки и строгого контроля состояния инициализации.