Service Workers представляют собой промежуточный слой между веб-приложением и сетью, позволяющий перехватывать сетевые запросы, кэшировать ответы и управлять офлайн-доступом. В контексте картографических приложений на базе Mapbox GL JS это критически важный механизм, поскольку карта состоит из множества сетевых ресурсов: тайлов, стилей, шрифтов, спрайтов и GeoJSON-данных.
Mapbox GL JS активно использует динамическую загрузку ресурсов, и без дополнительного уровня контроля каждое перемещение карты может приводить к десяткам или сотням запросов. Service Worker позволяет превратить эту модель в кэш-ориентированную систему с предсказуемой производительностью.
При стандартной работе Mapbox GL JS формирует запросы к следующим типам ресурсов:
.pbf).png, .jpg)style.json)sprite.json, sprite.png)Service Worker перехватывает эти запросы на уровне fetch
и принимает решение: вернуть кэшированную версию или обратиться к
сети.
Ключевая особенность заключается в том, что Mapbox GL JS не требует специальных адаптаций для работы с Service Worker — вся интеграция происходит на уровне сети.
Базовая регистрация выполняется в основном JavaScript-коде приложения:
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/sw.js', {
scope: '/'
});
}
После регистрации браузер начинает проксировать сетевые запросы через
sw.js, если они соответствуют области действия
(scope).
Основная логика строится вокруг события fetch:
self.addEventListener('fetch', (event) => {
const requestUrl = new URL(event.request.url);
if (requestUrl.pathname.includes('/tiles/')) {
event.respondWith(handleTileRequest(event.request));
return;
}
event.respondWith(fetch(event.request));
});
Такой подход позволяет выделять картографические ресурсы и обрабатывать их отдельно от остального трафика приложения.
Векторные тайлы являются наиболее частым источником сетевой нагрузки. Их кэширование даёт максимальный прирост производительности.
const TILE_CACHE = 'mapbox-tiles-v1';
async function handleTileRequest(request) {
const cache = await caches.open(TILE_CACHE);
const cached = await cache.match(request);
if (cached) {
return cached;
}
const response = await fetch(request);
cache.put(request, response.clone());
return response;
}
Особенность Mapbox GL JS заключается в том, что тайлы имеют предсказуемую структуру URL, что делает их удобными для кэширования по запросу.
Подходит для статических данных карты (например, базовые стили или редко обновляемые слои).
Преимущество заключается в высокой скорости отображения карты после первого посещения.
Используется для данных, которые часто обновляются, например:
async function networkFirst(request) {
const cache = await caches.open('dynamic-data');
try {
const response = await fetch(request);
cache.put(request, response.clone());
return response;
} catch (e) {
return cache.match(request);
}
}
Наиболее сбалансированная стратегия для картографических ресурсов:
async function staleWhileRevalidate(request) {
const cache = await caches.open('map-cache');
const cached = await cache.match(request);
const fetchPromise = fetch(request).then((response) => {
cache.put(request, response.clone());
return response;
});
return cached || fetchPromise;
}
Файлы стилей Mapbox GL JS представляют собой JSON-документы, описывающие слои, источники и визуальные параметры карты.
Особенность заключается в том, что стиль может ссылаться на множество внешних ресурсов, поэтому кэширование только JSON недостаточно.
const STYLE_CACHE = 'map-style';
self.addEventListener('fetch', (event) => {
if (event.request.url.includes('style.json')) {
event.respondWith(cacheFirst(event.request, STYLE_CACHE));
}
});
Важно учитывать версионирование стилей, так как изменение конфигурации слоёв может привести к несовместимости кэшированных тайлов.
Mapbox GL JS использует glyph-тайлы для рендеринга текста и спрайты для иконок.
Шрифты обычно запрашиваются по шаблону:
/fonts/{fontstack}/{range}.pbf
Спрайты:
/sprite.png
/sprite.json
Их кэширование особенно эффективно, так как они редко изменяются.
const STATIC_CACHE = 'map-static';
async function cacheFirst(request, cacheName) {
const cache = await caches.open(cacheName);
const cached = await cache.match(request);
if (cached) return cached;
const response = await fetch(request);
cache.put(request, response.clone());
return response;
}
Полноценная офлайн-карта требует предварительной загрузки регионов. Mapbox GL JS сам по себе не предоставляет полного офлайн-движка, поэтому Service Worker становится ключевым компонентом.
Стратегия включает:
async function precacheTiles(tileUrls) {
const cache = await caches.open('offline-map');
for (const url of tileUrls) {
const response = await fetch(url);
await cache.put(url, response);
}
}
Несмотря на гибкость Service Worker, существуют ограничения:
Особенно важно учитывать, что Mapbox GL JS может генерировать большое количество уникальных URL для тайлов, что увеличивает фрагментацию кэша.
При обновлении стилей или данных требуется контролируемая очистка кэша.
async function clearOldCaches() {
const keys = await caches.keys();
for (const key of keys) {
if (key.startsWith('map-') && key !== 'mapbox-tiles-v2') {
await caches.delete(key);
}
}
}
Версионирование кэша является основной стратегией управления обновлениями.
Service Worker работает в отдельном контексте и не имеет прямого доступа к DOM. В картографических приложениях это снижает риски, связанные с:
Однако необходимо учитывать, что любой закэшированный ресурс сохраняется в браузере и должен рассматриваться как потенциально долговременное состояние данных.
Оптимизация достигается за счёт:
На практике это влияет на:
Mapbox GL JS может обновлять источники данных в реальном времени. Service Worker должен учитывать этот факт, чтобы не блокировать свежие данные.
Комбинированная стратегия:
Такое разделение обеспечивает баланс между актуальностью и скоростью отображения.