Cache API

Mapbox GL JS опирается на многоуровневую систему загрузки и хранения данных, где ключевую роль играет кэширование ресурсов карты: тайлов, стилей, шрифтов (glyphs), спрайтов и метаданных источников. Cache API в браузере становится основным инструментом для контроля сетевых запросов и обеспечения офлайн-доступа, ускорения повторной отрисовки и снижения нагрузки на серверы картографического провайдера.


Архитектура загрузки ресурсов карты

При рендеринге карты Mapbox GL JS формирует множество параллельных запросов к внешним ресурсам:

  • векторные и растровые тайлы (tiles)
  • стиль карты (style.json)
  • спрайты (sprites)
  • шрифтовые глифы (glyphs)
  • источники данных (sources)

Каждый слой карты может инициировать собственные запросы, и именно здесь кэширование становится критическим элементом производительности.

Mapbox предоставляет CDN-инфраструктуру, однако на стороне клиента браузерный Cache API позволяет перехватывать и повторно использовать уже загруженные данные без повторных обращений к сети.


Cache API и его роль в браузерной экосистеме

Cache API — это интерфейс Web Platform, предназначенный для хранения HTTP-ответов в виде пар request/response. В контексте Mapbox GL JS он используется через Service Worker или промежуточные прокси-слои.

Основные особенности:

  • хранение ответов на уровне браузера
  • возможность программного контроля стратегии кэширования
  • работа в офлайн-режиме
  • изоляция по origin

Cache API не является автоматическим кэшем Mapbox GL JS — он подключается разработчиком при необходимости контроля сетевого поведения.


Кэширование тайлов

Тайлы являются наиболее частыми и тяжёлыми запросами в Mapbox GL JS. Они делятся на:

  • vector tiles (.pbf)
  • raster tiles (.png, .jpg, .webp)
  • terrain tiles (если используются DEM-слои)

Типичный URL тайла содержит координаты {z}/{x}/{y} и параметры стиля.

Базовая стратегия cache-first

self.addEventListener('fetch', (event) => {
  const url = new URL(event.request.url);

  if (url.pathname.includes('/tiles/')) {
    event.respondWith(
      caches.open('map-tiles-v1').then(async (cache) => {
        const cached = await cache.match(event.request);
        if (cached) return cached;

        const response = await fetch(event.request);
        cache.put(event.request, response.clone());
        return response;
      })
    );
  }
});

В этом сценарии:

  • первый запрос идёт в сеть
  • результат сохраняется в Cache Storage
  • повторные обращения обслуживаются локально

Кэширование стилей и ресурсов отображения

Стиль карты (style.json) определяет:

  • слои
  • источники данных
  • источники спрайтов
  • ссылки на glyphs

Эти ресурсы менее динамичны и идеально подходят для долгоживущего кэша.

Пример кэширования style.json

self.addEventListener('fetch', (event) => {
  if (event.request.url.includes('style.json')) {
    event.respondWith(
      caches.match(event.request).then((cached) => {
        if (cached) return cached;

        return fetch(event.request).then((response) => {
          return caches.open('map-styles-v1').then((cache) => {
            cache.put(event.request, response.clone());
            return response;
          });
        });
      })
    );
  }
});

Glyphs и спрайты

Glyphs (шрифты) и sprites часто представляют собой множественные мелкие запросы, чувствительные к задержкам сети.

Особенности кэширования:

  • glyph ranges запрашиваются динамически (/{fontstack}/{range}.pbf)
  • sprites делятся на JSON + PNG atlas
  • высокая эффективность cache-first стратегии

Оптимизация достигается через:

  • агрессивное кэширование sprite sheets
  • предварительную загрузку популярных диапазонов шрифтов
  • объединение запросов через HTTP/2

Offline режим карт

Полноценная офлайн-работа Mapbox GL JS строится поверх Cache API и Service Worker.

Базовая модель офлайн-режима

  1. предварительная загрузка области карты
  2. сохранение тайлов в Cache Storage
  3. перехват fetch-запросов
  4. возврат данных из локального хранилища
async function preCacheTiles(tileUrls) {
  const cache = await caches.open('offline-map-v1');

  await Promise.all(
    tileUrls.map(async (url) => {
      const response = await fetch(url);
      if (response.ok) {
        await cache.put(url, response);
      }
    })
  );
}

Интеграция Service Worker

Service Worker выступает центральным контроллером кэширования.

Он позволяет:

  • перехватывать все запросы Mapbox GL JS
  • реализовывать гибридные стратегии (network-first, cache-first)
  • управлять версиями данных

Network-first стратегия для динамических данных

self.addEventListener('fetch', (event) => {
  event.respondWith(
    fetch(event.request)
      .then((response) => {
        const cloned = response.clone();
        caches.open('dynamic-v1').then((cache) => {
          cache.put(event.request, cloned);
        });
        return response;
      })
      .catch(() => caches.match(event.request))
  );
});

Версионирование кэша

Mapbox GL JS активно использует версии стилей и источников данных. Это критично для предотвращения конфликтов старых и новых данных.

Практика:

  • добавление версии в имя cache storage
  • очистка устаревших кэшей при обновлении приложения
  • синхронизация с обновлениями стиля
const CACHE_VERSION = 'v3';
const CACHE_NAME = `mapbox-cache-${CACHE_VERSION}`;

self.addEventListener('activate', (event) => {
  event.waitUntil(
    caches.keys().then((keys) =>
      Promise.all(
        keys
          .filter((key) => key !== CACHE_NAME)
          .map((key) => caches.delete(key))
      )
    )
  );
});

Оптимизация производительности

Эффективное использование Cache API напрямую влияет на:

  • скорость initial render карты
  • плавность pan/zoom операций
  • количество сетевых запросов
  • расход трафика

Ключевые принципы:

  • приоритет кэша для тайлов высокого зума
  • ограничение размера Cache Storage
  • сегментация кэша по типам ресурсов
  • lazy caching для редко используемых регионов

Ограничения Cache API в картографических сценариях

Несмотря на гибкость, Cache API имеет ограничения:

  • отсутствие встроенной политики LRU
  • ограниченный контроль над размером хранилища
  • возможное удаление данных браузером
  • невозможность частичного обновления response
  • различия поведения между браузерами

В результате требуется дополнительный слой управления кэшем:

  • собственные индексы ресурсов
  • контроль возраста записей
  • ручная инвалидация

Гибридные стратегии кэширования

На практике Mapbox GL JS приложения используют комбинированные модели:

  • тайлы: cache-first
  • style.json: stale-while-revalidate
  • динамические данные: network-first
  • glyphs: cache-first с долгим TTL

Такая комбинация обеспечивает баланс между актуальностью данных и производительностью рендеринга.


Взаимодействие с рендерингом Mapbox GL JS

Кэширование влияет на внутренний pipeline рендера:

  • загрузка источников → блокировка слоя
  • доступность тайлов → прогресс отрисовки
  • обновление стиля → перерасчёт layout

При правильно настроенном Cache API:

  • уменьшается количество reflow операций
  • ускоряется загрузка viewport
  • снижается jitter при перемещении карты