При работе с Google Maps JavaScript API значительная часть производительности и пользовательского опыта определяется тем, насколько эффективно организовано повторное использование ранее полученных данных. Карты, маркеры, геокодирование, результаты поиска мест и географические запросы часто повторяются при перемещении по карте, изменении масштаба или возврате пользователя к уже посещённым областям.
Кэширование снижает:
Ключевой принцип заключается в том, что картографические данные обладают высокой степенью локальности: при смещении карты на небольшое расстояние большая часть информации остаётся неизменной.
Кэширование в экосистеме Google Maps JavaScript API можно разделить на несколько уровней:
1. Встроенное кэширование браузера Браузер автоматически кэширует:
Это кэширование контролируется HTTP-заголовками и не требует дополнительной логики со стороны разработчика.
2. Кэширование на уровне клиентского приложения Реализуется вручную:
localStoragesessionStorageIndexedDB3. Кэширование через Service Worker Используется для:
4. Серверное кэширование Применяется при проксировании запросов:
Геокодирование (Geocoding API) — одна из самых затратных операций, так как каждый запрос преобразует текстовый адрес в координаты или наоборот.
Типичный подход кэширования:
{ lat, lng, formatted_address, timestamp }Пример реализации in-memory и localStorage кэша:
const geocodeCache = new Map();
async function geocode(address) {
const key = address.trim().toLowerCase();
if (geocodeCache.has(key)) {
return geocodeCache.get(key);
}
const cached = localStorage.getItem(key);
if (cached) {
const parsed = JSON.parse(cached);
geocodeCache.set(key, parsed);
return parsed;
}
const response = await fetch(`/api/geocode?address=${encodeURIComponent(address)}`);
const data = await response.json();
const result = {
lat: data.lat,
lng: data.lng,
formatted: data.formatted,
timestamp: Date.now()
};
geocodeCache.set(key, result);
localStorage.setItem(key, JSON.stringify(result));
return result;
}
При работе с картой данные часто привязаны к координатам. Эффективный подход — разбиение мира на сетку.
Используются:
Пример кэширования по геохешу:
import ngeohash from "ngeohash";
const spatialCache = new Map();
function cacheKey(lat, lng, precision = 6) {
return ngeohash.encode(lat, lng, precision);
}
function setSpatialData(lat, lng, data) {
const key = cacheKey(lat, lng);
spatialCache.set(key, data);
}
function getSpatialData(lat, lng) {
const key = cacheKey(lat, lng);
return spatialCache.get(key);
}
Такой подход позволяет повторно использовать данные для соседних точек без повторных запросов.
При динамической отрисовке маркеров Google Maps важно избегать повторного создания объектов при каждом изменении состояния карты.
Подходы:
Пример:
const markerPool = new Map();
function getMarker(map, id, position) {
if (markerPool.has(id)) {
return markerPool.get(id);
}
const marker = new google.maps.Marker({
map,
position
});
markerPool.set(id, marker);
return marker;
}
При обновлении данных вместо пересоздания маркера выполняется обновление позиции:
function updateMarker(id, position) {
const marker = markerPool.get(id);
if (marker) {
marker.setPosition(position);
}
}
Результаты поиска мест часто повторяются при панорамировании карты или изменении масштаба.
Типичная стратегия:
query + bounds + typeПример:
const placesCache = new Map();
function buildKey(query, bounds) {
return `${query}:${bounds.getNorthEast().toUrlValue()}_${bounds.getSouthWest().toUrlValue()}`;
}
async function searchPlaces(service, request) {
const key = buildKey(request.query, request.bounds);
if (placesCache.has(key)) {
return placesCache.get(key);
}
return new Promise((resolve) => {
service.textSearch(request, (results, status) => {
if (status === "OK") {
placesCache.set(key, results);
resolve(results);
} else {
resolve([]);
}
});
});
}
При использовании Google Maps Platform необходимо учитывать ограничения на кэширование данных:
Это означает, что кэширование должно быть:
Тайлы карты — базовый слой визуализации. Браузеры уже используют HTTP-кэш, однако в сложных приложениях применяется дополнительная оптимизация:
Пример Service Worker:
self.addEventListener("fetch", (event) => {
const url = new URL(event.request.url);
if (url.hostname.includes("googleapis.com")) {
event.respondWith(
caches.open("maps-cache").then(async (cache) => {
const cached = await cache.match(event.request);
const fetchPromise = fetch(event.request).then((response) => {
cache.put(event.request, response.clone());
return response;
});
return cached || fetchPromise;
})
);
}
});
Самый быстрый способ хранения данных — оперативная память. Однако она требует контроля жизненного цикла.
Основные стратегии:
Пример TTL-кэша:
class TTLCache {
constructor(ttl = 60000) {
this.ttl = ttl;
this.store = new Map();
}
set(key, value) {
this.store.set(key, {
value,
expires: Date.now() + this.ttl
});
}
get(key) {
const item = this.store.get(key);
if (!item) return null;
if (Date.now() > item.expires) {
this.store.delete(key);
return null;
}
return item.value;
}
}
При каждом движении карты (bounds_changed,
idle) может происходить множество запросов.
Оптимизация включает:
Пример debounce:
function debounce(fn, delay) {
let timer;
return (...args) => {
clearTimeout(timer);
timer = setTimeout(() => fn(...args), delay);
};
}
map.addListener(
"bounds_changed",
debounce(() => {
const bounds = map.getBounds();
fetchDataForBounds(bounds);
}, 300)
);
Для хранения больших объёмов геоданных используется IndexedDB.
Преимущества:
Пример записи:
const request = indexedDB.open("maps-cache", 1);
request.onupgradenee ded = () => {
const db = request.result;
db.createObjectStore("places", { keyPath: "id" });
};
function savePlace(db, place) {
const tx = db.transaction("places", "readwrite");
tx.objectStore("places").put(place);
}
На практике используется несколько уровней одновременно:
Такая архитектура обеспечивает:
Основные сложности:
Решения:
function versionedKey(baseKey, version) {
return `${baseKey}:v${version}`;
}
Ключ кэша должен отражать:
Пример:
places:restaurants:6:9.123_45.678
Чем точнее ключ, тем выше точность данных, но ниже коэффициент повторного использования.
Типовая архитектура:
Такой слой позволяет изолировать Google Maps API от логики приложения и управлять всеми запросами централизованно.