До сих пор были лишь упоминания и крохотные фрагменты кода интерфейса Cache . Чтобы эффективно использовать сервис-воркеров, необходимо принять одну или несколько стратегий кэширования, что требует некоторого знакомства с интерфейсом Cache .
Стратегия кэширования — это взаимодействие между событием fetch сервисного работника и интерфейсом Cache . От того, как написана стратегия кэширования, зависит; например, может быть предпочтительнее обрабатывать запросы на статические ресурсы иначе, чем на документы, и это влияет на то, как формируется стратегия кэширования.
Прежде чем мы перейдем к самим стратегиям, давайте поговорим о том, чем не является интерфейс Cache , чем он является, а также кратко рассмотрим некоторые методы, которые он предлагает для управления кэшами сервисных работников.
Интерфейс Cache и HTTP-кеш
Если вы раньше не работали с интерфейсом Cache , у вас может возникнуть соблазн подумать, что он аналогичен HTTP-кешу или, по крайней мере, связан с ним. Это не так.
- Интерфейс
Cache— это механизм кэширования, полностью отдельный от кэша HTTP. - Какую бы конфигурацию
Cache-Controlвы ни использовали для воздействия на HTTP-кеш, она не влияет на то, какие ресурсы хранятся в интерфейсеCache.
Полезно представить кеши браузера как многоуровневые. Кэш HTTP — это низкоуровневый кеш, управляемый парами ключ-значение с директивами, выраженными в заголовках HTTP.
Напротив, интерфейс Cache представляет собой кеш высокого уровня, управляемый API JavaScript. Это обеспечивает большую гибкость, чем при использовании относительно упрощенных пар ключ-значение HTTP, и является половиной того, что делает возможными стратегии кэширования. Некоторые важные методы API для кэшей сервисных рабочих:
-
CacheStorage.openдля создания нового экземпляраCache. -
Cache.addиCache.putдля хранения сетевых ответов в кеше сервисного работника. -
Cache.matchдля поиска кэшированного ответа в экземпляреCache. -
Cache.deleteчтобы удалить кэшированный ответ из экземпляраCache.
Это лишь некоторые из них. Есть и другие полезные методы, но это основные, которые вы увидите позже в этом руководстве.
Скромное событие fetch
Другая половина стратегии кэширования — это событие fetch сервисного работника. До сих пор в этой документации вы немного слышали о «перехвате сетевых запросов», и это происходит в событии fetch внутри сервис-воркера:
// Establish a cache name const cacheName = 'MyFancyCacheName_v1'; self.addEventListener('install', (event) => { event.waitUntil(caches.open(cacheName)); }); self.addEventListener('fetch', async (event) => { // Is this a request for an image? if (event.request.destination === 'image') { // Open the cache event.respondWith(caches.open(cacheName).then((cache) => { // Respond with the image from the cache or from the network return cache.match(event.request).then((cachedResponse) => { return cachedResponse || fetch(event.request.url).then((fetchedResponse) => { // Add the network response to the cache for future visits. // Note: we need to make a copy of the response to save it in // the cache and use the original as the request response. cache.put(event.request, fetchedResponse.clone()); // Return the network response return fetchedResponse; }); }); })); } else { return; } }); Это игрушечный пример, который вы можете увидеть в действии самостоятельно , но он дает представление о том, на что способны сервисные работники. Приведенный выше код делает следующее:
- Проверьте свойство
destinationзапроса, чтобы определить, является ли это запросом изображения. - Если изображение находится в кеше сервис-воркера, обслуживайте его оттуда. Если нет, извлеките изображение из сети, сохраните ответ в кеше и верните ответ сети.
- Все остальные запросы передаются через сервис-воркера без взаимодействия с кешем.
Объект event выборки содержит свойство request , которое содержит некоторую полезную информацию, которая поможет вам определить тип каждого запроса:
-
url— URL-адрес сетевого запроса, который в данный момент обрабатывается событиемfetch. -
method, который является методом запроса (например,GETилиPOST). -
mode, который описывает режим запроса. Значение'navigate'часто используется, чтобы отличать запросы HTML-документов от других запросов. -
destination, который описывает тип запрашиваемого контента таким образом, чтобы избежать использования расширения файла запрошенного ресурса.
Еще раз: асинхронность — это название игры. Вы помните, что событие install предлагает метод event.waitUntil , который принимает обещание и ожидает его разрешения, прежде чем продолжить активацию. Событие fetch предлагает аналогичный метод event.respondWith , который можно использовать для возврата результата запроса асинхронной fetch или ответа, возвращаемого методом match интерфейса Cache .
Стратегии кэширования
Теперь, когда вы немного знакомы с экземплярами Cache и обработчиком событий fetch , вы готовы погрузиться в некоторые стратегии кэширования сервис-воркеров. Хотя возможности практически безграничны, в этом руководстве будут рассмотрены стратегии, поставляемые с Workbox, так что вы сможете получить представление о том, что происходит внутри Workbox.
Только кэш

Начнем с простой стратегии кэширования, которую мы назовем «Только кэш». Дело в том, что когда сервис-воркер контролирует страницу, соответствующие запросы будут поступать только в кеш. Это означает, что любые кэшированные ресурсы необходимо будет предварительно кэшировать, чтобы они были доступны для работы шаблона, и что эти активы никогда не будут обновляться в кеше до тех пор, пока не будет обновлен сервисный работник.
// Establish a cache name const cacheName = 'MyFancyCacheName_v1'; // Assets to precache const precachedAssets = [ '/possum1.jpg', '/possum2.jpg', '/possum3.jpg', '/possum4.jpg' ]; self.addEventListener('install', (event) => { // Precache assets on install event.waitUntil(caches.open(cacheName).then((cache) => { return cache.addAll(precachedAssets); })); }); self.addEventListener('fetch', (event) => { // Is this one of our precached assets? const url = new URL(event.request.url); const isPrecachedRequest = precachedAssets.includes(url.pathname); if (isPrecachedRequest) { // Grab the precached asset from the cache event.respondWith(caches.open(cacheName).then((cache) => { return cache.match(event.request.url); })); } else { // Go to the network return; } }); Выше: массив ресурсов предварительно кэшируется во время установки. Когда сервис-воркер обрабатывает выборку, мы проверяем, находится ли URL-адрес запроса, обрабатываемый событием fetch , в массиве предварительно кэшированных ресурсов. Если да, мы извлекаем ресурс из кеша и пропускаем сеть. Остальные запросы передаются в сеть и только в сеть. Чтобы увидеть эту стратегию в действии, посмотрите эту демонстрацию с открытой консолью.
Только сеть

Противоположностью «Только кэша» является «Только сеть», когда запрос передается через сервис-воркера в сеть без какого-либо взаимодействия с кэшем сервис-воркера. Это хорошая стратегия для обеспечения актуальности контента (например, разметка), но компромиссом является то, что она никогда не будет работать, когда пользователь не в сети.
Обеспечение прохождения запроса в сеть означает, что вы не вызываете event.respondWith для соответствующего запроса. Если вы хотите быть явным, вы можете указать пустой return; в обратном вызове события fetch для запросов, которые вы хотите передать в сеть. Именно это происходит в демонстрации стратегии «Только кэширование» для запросов, которые не кэшируются предварительно.
Сначала кэшируйте, затем возвращайтесь к сети

В этой стратегии все становится немного сложнее. Для сопоставления запросов процесс выглядит следующим образом:
- Запрос попадает в кеш. Если ресурс находится в кеше, обслуживайте его оттуда.
- Если запроса нет в кеше, зайдите в сеть.
- После завершения сетевого запроса добавьте его в кеш, а затем верните ответ из сети.
Вот пример этой стратегии, который вы можете проверить в живой демо-версии :
// Establish a cache name const cacheName = 'MyFancyCacheName_v1'; self.addEventListener('fetch', (event) => { // Check if this is a request for an image if (event.request.destination === 'image') { event.respondWith(caches.open(cacheName).then((cache) => { // Go to the cache first return cache.match(event.request.url).then((cachedResponse) => { // Return a cached response if we have one if (cachedResponse) { return cachedResponse; } // Otherwise, hit the network return fetch(event.request).then((fetchedResponse) => { // Add the network response to the cache for later visits cache.put(event.request, fetchedResponse.clone()); // Return the network response return fetchedResponse; }); }); })); } else { return; } }); Хотя этот пример охватывает только изображения, это отличная стратегия, которую можно применить ко всем статическим ресурсам (таким как CSS, JavaScript, изображения и шрифты), особенно к хеш-версиям. Он обеспечивает повышение скорости работы неизменяемых ресурсов за счет обхода любых проверок актуальности контента на сервере, который может запустить HTTP-кеш. Что еще более важно, любые кэшированные ресурсы будут доступны в автономном режиме.
Сначала сеть, возвращаясь к кешу

Если вы перевернете принцип «сначала кэш, потом сеть» с ног на голову, вы получите стратегию «сначала сеть, потом кэш», вот как это звучит:
- Сначала вы заходите в сеть за запросом и помещаете ответ в кеш.
- Если позже вы отключитесь от сети, вы вернетесь к последней версии этого ответа в кеше.
Эта стратегия отлично подходит для запросов HTML или API, когда в режиме онлайн вам нужна самая последняя версия ресурса, но вы хотите предоставить автономный доступ к самой последней доступной версии. Вот как это может выглядеть применительно к запросам HTML:
// Establish a cache name const cacheName = 'MyFancyCacheName_v1'; self.addEventListener('fetch', (event) => { // Check if this is a navigation request if (event.request.mode === 'navigate') { // Open the cache event.respondWith(caches.open(cacheName).then((cache) => { // Go to the network first return fetch(event.request.url).then((fetchedResponse) => { cache.put(event.request, fetchedResponse.clone()); return fetchedResponse; }).catch(() => { // If the network is unavailable, get return cache.match(event.request.url); }); })); } else { return; } }); Вы можете попробовать это в демо-версии . Сначала зайдите на страницу. Возможно, вам придется перезагрузить компьютер, прежде чем ответ HTML будет помещен в кеш. Затем в инструментах разработчика смоделируйте автономное соединение и снова перезагрузите компьютер. Последняя доступная версия будет мгновенно загружена из кэша.
В ситуациях, когда возможность работы в автономном режиме важна, но вам необходимо сбалансировать эту возможность с доступом к самой последней версии разметки или данных API, стратегия «сначала сеть, потом кэш» — это надежная стратегия, позволяющая достичь этой цели.
Устаревшие при повторной проверке

Из стратегий, которые мы рассмотрели до сих пор, стратегия «Устаревшие при повторной проверке» является самой сложной. В чем-то она похожа на две последние стратегии, но в этой процедуре приоритет отдается скорости доступа к ресурсу, а также поддерживается его актуальность в фоновом режиме. Эта стратегия выглядит примерно так:
- При первом запросе актива извлеките его из сети, поместите в кеш и верните ответ сети.
- При последующих запросах сначала обслуживайте актив из кэша, затем «в фоновом режиме» повторно запросите его из сети и обновите запись в кэше актива.
- Для запросов после этого вы получите последнюю версию, полученную из сети, которая была помещена в кэш на предыдущем шаге.
Это отличная стратегия для вещей, которые важно поддерживать в курсе событий, но не имеют решающего значения. Подумайте о таких вещах, как аватары для социальных сетей. Они обновляются, когда пользователи это делают, но последняя версия не является строго необходимой для каждого запроса.
// Establish a cache name const cacheName = 'MyFancyCacheName_v1'; self.addEventListener('fetch', (event) => { if (event.request.destination === 'image') { event.respondWith(caches.open(cacheName).then((cache) => { return cache.match(event.request).then((cachedResponse) => { const fetchedResponse = fetch(event.request).then((networkResponse) => { cache.put(event.request, networkResponse.clone()); return networkResponse; }); return cachedResponse || fetchedResponse; }); })); } else { return; } }); Вы можете увидеть это в действии в еще одной живой демонстрации , особенно если вы обратите внимание на вкладку сети в инструментах разработчика вашего браузера и на ее средство просмотра CacheStorage (если в инструментах разработчика вашего браузера есть такой инструмент).
Вперед в Workbox!
В этом документе завершается наш обзор API сервис-воркеров, а также связанных с ними API, что означает, что вы узнали достаточно о том, как напрямую использовать сервис-воркеров, чтобы начать работать с Workbox!