← Назад к вопросам

Как работает кэширование через Service Worker?

2.0 Middle🔥 161 комментариев
#JavaScript Core

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI3 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Кэширование через Service Worker

Service Worker - это скрипт, работающий в фоне браузера отдельно от основной страницы. Он может перехватывать сетевые запросы и кэшировать данные для оффлайн работы.

Жизненный цикл Service Worker

Service Worker проходит несколько фаз:

// 1. INSTALL фаза
self.addEventListener("install", (event) => {
  console.log("Service Worker установлен");
  // Здесь кэшируем ресурсы при первой установке
  event.waitUntil(
    caches.open("v1").then((cache) => {
      return cache.addAll([
        "/",
        "/index.html",
        "/styles.css",
        "/app.js"
      ]);
    })
  );
});

// 2. ACTIVATE фаза
self.addEventListener("activate", (event) => {
  console.log("Service Worker активирован");
  // Очищаем старые версии кэша
  event.waitUntil(
    caches.keys().then((cacheNames) => {
      return Promise.all(
        cacheNames.map((cacheName) => {
          if (cacheName !== "v1") {
            return caches.delete(cacheName);
          }
        })
      );
    })
  );
});

// 3. FETCH фаза (перехват запросов)
self.addEventListener("fetch", (event) => {
  console.log("Перехвачен запрос", event.request.url);
  // Стратегия: кэш сначала, потом сеть
  event.respondWith(
    caches.match(event.request).then((response) => {
      return response || fetch(event.request);
    })
  );
});

Стратегии кэширования

Стратегия 1: Cache First (Кэш в приоритете)

Используй для статических ресурсов:

self.addEventListener("fetch", (event) => {
  event.respondWith(
    caches.match(event.request).then((response) => {
      // Если в кэше - вернуть из кэша
      if (response) {
        return response;
      }
      // Если нет в кэше - получить с сервера
      return fetch(event.request).then((response) => {
        // Кэшировать новый ответ
        if (!response || response.status !== 200) {
          return response;
        }
        const clonedResponse = response.clone();
        caches.open("v1").then((cache) => {
          cache.put(event.request, clonedResponse);
        });
        return response;
      });
    })
  );
});

Стратегия 2: Network First (Сеть в приоритете)

Используй для API запросов и контента:

self.addEventListener("fetch", (event) => {
  event.respondWith(
    fetch(event.request)
      .then((response) => {
        // Успешный ответ - кэшировать и вернуть
        if (response && response.status === 200) {
          const clonedResponse = response.clone();
          caches.open("api-v1").then((cache) => {
            cache.put(event.request, clonedResponse);
          });
        }
        return response;
      })
      .catch(() => {
        // Нет сети - использовать кэш
        return caches.match(event.request).then((response) => {
          return response || new Response("Offline", {
            status: 503,
            statusText: "Service Unavailable"
          });
        });
      })
  );
});

Стратегия 3: Stale While Revalidate

Вернуть кэш сразу, параллельно обновить:

self.addEventListener("fetch", (event) => {
  event.respondWith(
    caches.open("v1").then((cache) => {
      return cache.match(event.request).then((response) => {
        // Параллельно получаем свежие данные с сервера
        const fetchPromise = fetch(event.request).then((response) => {
          // Обновляем кэш в фоне
          if (response && response.status === 200) {
            cache.put(event.request, response.clone());
          }
          return response;
        });

        // Вернуть кэш сразу, или ждать свежих данных
        return response || fetchPromise;
      });
    })
  );
});

Регистрация Service Worker в приложении

// На фронтенде (main.js или index.js)
if ("serviceWorker" in navigator) {
  window.addEventListener("load", () => {
    navigator.serviceWorker.register("/service-worker.js").then(
      (registration) => {
        console.log("Service Worker зарегистрирован", registration);
      },
      (error) => {
        console.error("Service Worker не зарегистрирован", error);
      }
    );
  });
}

Работа с Cache API

// Открыть или создать кэш
const cache = await caches.open("v1");

// Добавить ресурсы в кэш
await cache.addAll([
  "/index.html",
  "/styles.css",
  "/app.js"
]);

// Добавить одиночный запрос
await cache.add("/page.html");

// Добавить запрос с ответом
await cache.put(
  new Request("/data.json"),
  new Response(JSON.stringify({ data: "value" }))
);

// Получить из кэша
const response = await cache.match("/index.html");

// Удалить из кэша
await cache.delete("/page.html");

// Удалить весь кэш
await caches.delete("v1");

// Список всех кэшей
const cacheNames = await caches.keys();
console.log(cacheNames);

Практический пример с версионированием

const CACHE_VERSION = "v2";
const CACHE_NAME = `my-app-${CACHE_VERSION}`;
const URLS_TO_CACHE = [
  "/",
  "/index.html",
  "/styles.css",
  "/app.js",
  "/offline.html"
];

self.addEventListener("install", (event) => {
  event.waitUntil(
    caches.open(CACHE_NAME).then((cache) => {
      return cache.addAll(URLS_TO_CACHE);
    })
  );
  // Пропустить ожидание, сразу активировать
  self.skipWaiting();
});

self.addEventListener("activate", (event) => {
  event.waitUntil(
    caches.keys().then((cacheNames) => {
      return Promise.all(
        cacheNames.map((cacheName) => {
          if (!cacheName.startsWith("my-app-")) return;
          if (cacheName !== CACHE_NAME) {
            return caches.delete(cacheName);
          }
        })
      );
    })
  );
  // Контролировать все клиенты сразу
  self.clients.claim();
});

self.addEventListener("fetch", (event) => {
  const url = new URL(event.request.url);
  
  // API запросы - network first
  if (url.pathname.startsWith("/api/")) {
    event.respondWith(
      fetch(event.request)
        .then(response => {
          const cache = caches.open(CACHE_NAME);
          cache.then(c => c.put(event.request, response.clone()));
          return response;
        })
        .catch(() => caches.match(event.request))
    );
  }
  // Статические файлы - cache first
  else {
    event.respondWith(
      caches.match(event.request).then((response) => {
        return response || fetch(event.request);
      }).catch(() => caches.match("/offline.html"))
    );
  }
});

Важные моменты

  • Service Worker работает только на HTTPS (кроме localhost)
  • Область действия ограничена путем регистрации
  • Можно иметь несколько кэшей с разными именами
  • Обновление Service Worker требует явной переустановки
  • Используй версионирование для управления кэшем
  • Для тестирования: Chrome DevTools -> Application -> Service Workers
Как работает кэширование через Service Worker? | PrepBro