Обработчик cacheFirst

Стратегия cacheFirst относится к числу базовых подходов работы Service Worker с кэшем. Её основная идея — приоритетное использование данных из кэша перед обращением к сети. Это позволяет значительно ускорить загрузку ресурсов и уменьшить зависимость от качества интернет-соединения.

Алгоритм работы:

  1. Проверка наличия ресурса в кэше.
  2. Если ресурс найден — возврат из кэша.
  3. Если ресурса нет — выполнение сетевого запроса.
  4. Полученный ответ сохраняется в кэш для последующих обращений.

Такой подход особенно эффективен для статических ресурсов, которые редко изменяются.


Назначение cacheFirst в sw-precache

Библиотека sw-precache автоматически генерирует Service Worker с преднастроенными стратегиями кэширования. Обработчик cacheFirst применяется для:

  • статических файлов (CSS, JS, изображения)
  • шрифтов
  • библиотек
  • ассетов, версия которых редко меняется

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


Базовая реализация cacheFirst

Пример реализации стратегии вручную внутри Service Worker:

self.addEventListener('fetch', function(event) {
  event.respondWith(
    caches.match(event.request).then(function(response) {
      if (response) {
        return response;
      }

      return fetch(event.request).then(function(networkResponse) {
        return caches.open('runtime-cache').then(function(cache) {
          cache.put(event.request, networkResponse.clone());
          return networkResponse;
        });
      });
    })
  );
});

Ключевые элементы:

  • caches.match() — поиск ресурса в кэше
  • fetch() — сетевой запрос при отсутствии кэша
  • cache.put() — сохранение ответа

Настройка cacheFirst в sw-precache

В sw-precache стратегия задаётся через параметр runtimeCaching.

Пример конфигурации:

module.exports = {
  runtimeCaching: [
    {
      urlPattern: /\.(?:png|jpg|jpeg|svg|gif)$/,
      handler: 'cacheFirst'
    }
  ]
};

Здесь:

  • urlPattern — регулярное выражение для выбора ресурсов
  • handler: 'cacheFirst' — применение стратегии

Расширенная конфигурация

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

module.exports = {
  runtimeCaching: [
    {
      urlPattern: /^https:\/\/fonts\.googleapis\.com\//,
      handler: 'cacheFirst',
      options: {
        cacheName: 'google-fonts',
        cacheExpiration: {
          maxEntries: 30,
          maxAgeSeconds: 60 * 60 * 24 * 365
        }
      }
    }
  ]
};

Параметры:

  • cacheName — имя кэша
  • maxEntries — максимальное количество записей
  • maxAgeSeconds — время жизни ресурсов

Поведение при обновлении ресурсов

Ограничение стратегии cacheFirst заключается в том, что:

  • при наличии ресурса в кэше сеть не используется
  • обновления на сервере игнорируются до очистки кэша

Это приводит к проблеме устаревших данных.

Способы решения:

  1. Версионирование файлов:

    app.js?v=2
  2. Использование хешей:

    app.8f3a1c.js
  3. Ограничение времени жизни через maxAgeSeconds


Сравнение с другими стратегиями

Стратегия Приоритет Использование сети Актуальность данных
cacheFirst Кэш Только при промахе Низкая
networkFirst Сеть Всегда Высокая
staleWhileRevalidate Кэш + обновление Фоновая Средняя

cacheFirst обеспечивает максимальную скорость, но уступает в актуальности.


Практические сценарии применения

1. Изображения

{
  urlPattern: /\.(?:png|jpg|jpeg|svg|gif)$/,
  handler: 'cacheFirst'
}

2. Шрифты

{
  urlPattern: /^https:\/\/fonts\./,
  handler: 'cacheFirst'
}

3. JS и CSS

{
  urlPattern: /\.(?:js|css)$/,
  handler: 'cacheFirst'
}

Управление размером кэша

Без ограничений cacheFirst может привести к переполнению хранилища.

Рекомендуемые настройки:

options: {
  cacheExpiration: {
    maxEntries: 50,
    maxAgeSeconds: 7 * 24 * 60 * 60
  }
}

Подходы:

  • ограничение количества файлов
  • удаление устаревших записей
  • разделение кэшей по типам ресурсов

Влияние на производительность

Преимущества:

  • мгновенный доступ к данным
  • снижение нагрузки на сеть
  • улучшение показателей Lighthouse (Performance)

Недостатки:

  • возможное устаревание данных
  • необходимость контроля версии ресурсов

Особенности работы в офлайн-режиме

cacheFirst обеспечивает:

  • доступ к закэшированным ресурсам без сети
  • стабильную работу интерфейса
  • быструю загрузку повторных посещений

Ограничения:

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

Комбинирование с другими стратегиями

На практике cacheFirst редко используется изолированно.

Типичная схема:

runtimeCaching: [
  {
    urlPattern: /\.(?:js|css|png|jpg)$/,
    handler: 'cacheFirst'
  },
  {
    urlPattern: /\/api\//,
    handler: 'networkFirst'
  }
]

Подход:

  • статические ресурсы → cacheFirst
  • API → networkFirst

Ошибки и подводные камни

1. Кэширование HTML Использование cacheFirst для HTML может привести к устаревшему интерфейсу.

2. Отсутствие версионирования Файлы не обновляются после деплоя.

3. Переполнение кэша Без ограничений хранилище быстро заполняется.

4. Неправильные urlPattern Слишком широкие шаблоны могут кэшировать нежелательные ресурсы.


Рекомендации по использованию

  • применять только для статических ресурсов
  • использовать версионирование файлов
  • ограничивать размер кэша
  • избегать использования для API и HTML
  • комбинировать с другими стратегиями

Внутренний механизм sw-precache

При генерации Service Worker библиотека:

  1. Создаёт список ресурсов для кэширования
  2. Добавляет обработчик fetch
  3. Вставляет логику cacheFirst
  4. Управляет кэшами автоматически

Пример сгенерированного фрагмента:

self.addEventListener('fetch', function(event) {
  if (event.request.url.match(/\.png$/)) {
    event.respondWith(
      caches.open('cache-name').then(function(cache) {
        return cache.match(event.request).then(function(response) {
          return response || fetch(event.request).then(function(resp) {
            cache.put(event.request, resp.clone());
            return resp;
          });
        });
      })
    );
  }
});

Контроль и отладка

Инструменты:

  • Chrome DevTools → Application → Cache Storage
  • вкладка Network (проверка источника ответа)
  • отключение сети для тестирования офлайн-режима

Проверка:

  • ресурс загружается из Service Worker
  • статус from ServiceWorker или from disk cache

Производственные практики

  • разделение кэшей по версиям
  • автоматическая очистка старых кэшей
  • использование hash-based имен файлов
  • минимизация количества runtime-запросов

Пример удаления старых кэшей:

self.addEventListener('activate', function(event) {
  const whitelist = ['cache-v2'];

  event.waitUntil(
    caches.keys().then(function(keys) {
      return Promise.all(
        keys.map(function(key) {
          if (!whitelist.includes(key)) {
            return caches.delete(key);
          }
        })
      );
    })
  );
});

Архитектурная роль cacheFirst

Стратегия занимает ключевое место в построении offline-first приложений, обеспечивая:

  • мгновенную загрузку интерфейса
  • устойчивость к нестабильной сети
  • снижение нагрузки на сервер

При правильной настройке cacheFirst становится основой производительного и отзывчивого веб-приложения.