Кэширование ресурсов во время выполнения

Некоторые ресурсы вашего веб-приложения могут использоваться нечасто, иметь очень большой размер или варьироваться в зависимости от устройства пользователя (например, адаптивные изображения) или языка. Это случаи, когда предварительное кэширование может быть антишаблоном , и вместо этого вам следует полагаться на кэширование во время выполнения.

В Workbox вы можете управлять кэшированием ресурсов во время выполнения с помощью модуля workbox-routing для сопоставления маршрутов, а также управлять стратегиями кэширования для них с помощью модуля workbox-strategies .

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

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

  • Stale While Revalidate использует кэшированный ответ на запрос, если он доступен, и обновляет кеш в фоновом режиме ответом из сети. Следовательно, если ресурс не кэширован, он будет ждать ответа сети и использовать его. Это довольно безопасная стратегия, поскольку она регулярно обновляет записи кэша, которые на нее полагаются. Недостатком является то, что он всегда запрашивает актив из сети в фоновом режиме.
  • Network First пытается сначала получить ответ от сети. Если получен ответ, он передает этот ответ браузеру и сохраняет его в кеше. Если сетевой запрос завершается неудачей, будет использован последний кэшированный ответ, обеспечивающий автономный доступ к активу.
  • Cache First сначала проверяет кеш на наличие ответа и использует его, если он доступен. Если запрос отсутствует в кеше, используется сеть, и любой действительный ответ добавляется в кеш перед передачей в браузер.
  • Только сеть заставляет ответ поступать из сети.
  • Cache Only заставляет ответ поступать из кеша.

Вы можете применять эти стратегии для выбора запросов, используя методы, предлагаемые workbox-routing .

Применение стратегий кэширования с сопоставлением маршрутов

workbox-routing предоставляет метод registerRoute для сопоставления маршрутов и обработки их с помощью стратегии кэширования. registerRoute принимает объект Route , который, в свою очередь, принимает два аргумента:

  1. Строка, регулярное выражение или обратный вызов сопоставления для указания критериев соответствия маршрута.
  2. Обработчик маршрута — обычно стратегия, предоставляемая workbox-strategies .

Обратные вызовы сопоставления предпочтительнее для сопоставления маршрутов, поскольку они предоставляют объект контекста, включающий объект Request , строку URL-адреса запроса, событие выборки и логическое значение, указывающее, является ли запрос запросом того же источника.

Затем обработчик обрабатывает сопоставленный маршрут. В следующем примере создается новый маршрут, который соответствует поступающим запросам изображений того же источника, сначала применяя кеш, а затем возвращаясь к сетевой стратегии .

// sw.js
import { registerRoute, Route } from 'workbox-routing';
import { CacheFirst } from 'workbox-strategies';

// A new route that matches same-origin image requests and handles
// them with the cache-first, falling back to network strategy:
const imageRoute = new Route(({ request, sameOrigin }) => {
  return sameOrigin && request.destination === 'image'
}, new CacheFirst());

// Register the new route
registerRoute(imageRoute);

Использование нескольких кэшей

Workbox позволяет группировать кэшированные ответы в отдельные экземпляры Cache , используя cacheName , доступную в связанных стратегиях.

В следующем примере изображения используют стратегию «устаревшие при повторной проверке», тогда как ресурсы CSS и JavaScript используют стратегию «сначала кэширования», возвращающуюся к сетевой стратегии. Маршрут для каждого ресурса помещает ответы в отдельные кэши путем добавления свойства cacheName .

// sw.js
import { registerRoute, Route } from 'workbox-routing';
import { CacheFirst, StaleWhileRevalidate } from 'workbox-strategies';

// Handle images:
const imageRoute = new Route(({ request }) => {
  return request.destination === 'image'
}, new StaleWhileRevalidate({
  cacheName: 'images'
}));

// Handle scripts:
const scriptsRoute = new Route(({ request }) => {
  return request.destination === 'script';
}, new CacheFirst({
  cacheName: 'scripts'
}));

// Handle styles:
const stylesRoute = new Route(({ request }) => {
  return request.destination === 'style';
}, new CacheFirst({
  cacheName: 'styles'
}));

// Register routes
registerRoute(imageRoute);
registerRoute(scriptsRoute);
registerRoute(stylesRoute);
Снимок экрана со списком экземпляров Cache на вкладке приложений в Chrome DevTools. Показаны три отдельных кэша: один называется «скрипты», другой — «стили», а последний — «изображения».
Средство просмотра хранилища кэша на панели приложений Chrome DevTools. Ответы для разных типов активов хранятся в отдельных кэшах.

Установка срока действия для записей кэша

Помните о квотах хранилища при управлении кэшами Service Worker. ExpirationPlugin упрощает обслуживание кэша и предоставляется с помощью workbox-expiration . Чтобы использовать его, укажите его в конфигурации стратегии кэширования:

// sw.js
import { registerRoute, Route } from 'workbox-routing';
import { CacheFirst } from 'workbox-strategies';
import { ExpirationPlugin } from 'workbox-expiration';

// Evict image cache entries older thirty days:
const imageRoute = new Route(({ request }) => {
  return request.destination === 'image';
}, new CacheFirst({
  cacheName: 'images',
  plugins: [
    new ExpirationPlugin({
      maxAgeSeconds: 60 * 60 * 24 * 30,
    })
  ]
}));

// Evict the least-used script cache entries when
// the cache has more than 50 entries:
const scriptsRoute = new Route(({ request }) => {
  return request.destination === 'script';
}, new CacheFirst({
  cacheName: 'scripts',
  plugins: [
    new ExpirationPlugin({
      maxEntries: 50,
    })
  ]
}));

// Register routes
registerRoute(imageRoute);
registerRoute(scriptsRoute);

Соблюдение квот хранения может быть сложным. Хорошей практикой является рассмотрение пользователей, которые могут испытывать нехватку места или хотят наиболее эффективно использовать свое хранилище. Пары ExpirationPlugin Workbox могут помочь в достижении этой цели.

Соображения о перекрестном происхождении

Взаимодействие между вашим сервисным работником и ресурсами перекрестного происхождения значительно отличается от взаимодействия с активами одного происхождения. Совместное использование ресурсов между источниками (CORS) сложно, и эта сложность распространяется на то, как вы обрабатываете ресурсы из разных источников в сервисном работнике.

Непрозрачные ответы

При выполнении запроса между источниками в режиме no-cors ответ может храниться в кеше сервисного работника и даже использоваться непосредственно браузером. Однако само тело ответа невозможно прочитать с помощью JavaScript. Это известно как непрозрачный ответ .

Непрозрачные ответы — это мера безопасности, призванная предотвратить проверку активов перекрестного происхождения. Вы по-прежнему можете делать запросы к ресурсам перекрестного происхождения и даже кэшировать их, вы просто не можете прочитать тело ответа или даже прочитать его код состояния !

Не забудьте включить режим CORS.

Даже если вы загружаете ресурсы с перекрестным происхождением, которые устанавливают разрешающие заголовки CORS, позволяющие вам читать ответы, тело ответа с перекрестным происхождением все равно может быть непрозрачным. Например, следующий HTML-код вызовет запросы no-cors , которые приведут к непрозрачным ответам независимо от того, какие заголовки CORS установлены:

<link rel="stylesheet" href="https://example.com/path/to/style.css">
<img src="https://example.com/path/to/image.png">

Чтобы явно запустить запрос cors , который даст непрозрачный ответ, вам необходимо явно выбрать режим CORS, добавив атрибут crossorigin в ваш HTML:

<link crossorigin="anonymous" rel="stylesheet" href="https://example.com/path/to/style.css">
<img crossorigin="anonymous" src="https://example.com/path/to/image.png">

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

Workbox может не кэшировать непрозрачные ответы.

По умолчанию Workbox осторожно подходит к кэшированию непрозрачных ответов. Поскольку невозможно проверить код ответа на наличие непрозрачных ответов, кэширование ответа об ошибке может привести к постоянным сбоям в работе, если используется стратегия «сначала кэш» или «только кэш».

Если вам необходимо кэшировать непрозрачный ответ в Workbox, для его обработки следует использовать стратегию «сначала сеть» или «устаревшую во время проверки». Да, это означает, что ресурс по-прежнему будет запрашиваться из сети каждый раз, но это гарантирует, что неудавшиеся ответы не сохранятся и в конечном итоге будут заменены пригодными для использования ответами.

Если вы используете другую стратегию кэширования и возвращается непрозрачный ответ, Workbox предупредит вас, что ответ не был кэширован в режиме разработки .

Принудительное кэширование непрозрачных ответов

Если вы абсолютно уверены , что хотите кэшировать непрозрачный ответ, используя стратегию «сначала кэш» или «только кэш», вы можете заставить Workbox сделать это с помощью модуля workbox-cacheable-response :

import {Route, registerRoute} from 'workbox-routing';
import {NetworkFirst, StaleWhileRevalidate} from 'workbox-strategies';
import {CacheableResponsePlugin} from 'workbox-cacheable-response';

const cdnRoute = new Route(({url}) => {
  return url === 'https://cdn.google.com/example-script.min.js';
}, new CacheFirst({
  plugins: [
    new CacheableResponsePlugin({
      statuses: [0, 200]
    })
  ]
}))

registerRoute(cdnRoute);

Непрозрачные ответы и API navigator.storage

Чтобы избежать утечки междоменной информации, к размеру непрозрачного ответа, используемого для расчета пределов квоты хранилища, добавляется значительное дополнение. Это влияет на то, как API navigator.storage сообщает о квотах хранилища.

Это заполнение зависит от браузера, но для Chrome минимальный размер, который любой один кэшированный непрозрачный ответ вносит в общий объем используемого хранилища, составляет примерно 7 мегабайт . Вы должны помнить об этом при определении количества непрозрачных ответов, которые вы хотите кэшировать, поскольку вы можете легко превысить квоты хранилища гораздо раньше, чем в противном случае ожидали бы.