Обслуживание

Ключевым аспектом прогрессивных веб-приложений является их надёжность: они могут быстро загружать ресурсы, поддерживая интерес пользователей и мгновенно предоставляя обратную связь даже при слабом сетевом соединении. Как это возможно? Благодаря событию fetch сервис-воркера.

Событие выборки

Browser Support

  • Хром: 40.
  • Край: 17.
  • Firefox: 44.
  • Сафари: 11.1.

Source

Событие fetch позволяет перехватывать все сетевые запросы, выполняемые PWA в области действия сервис-воркера, как из одного источника, так и из разных источников. Помимо запросов навигации и ресурсов, извлечение данных из установленного сервис-воркера позволяет обрабатывать посещения страниц после первой загрузки сайта без сетевых вызовов.

Обработчик fetch получает все запросы от приложения, включая URL-адреса и заголовки HTTP, и позволяет разработчику приложения решать, как их обрабатывать.

Сервисный работник находится между клиентом и сетью.

Ваш сервис-воркер может переслать запрос в сеть, ответить ранее кэшированным ответом или создать новый ответ. Выбор за вами. Вот простой пример:

self.addEventListener("fetch", event => {
    console.log(`URL requested: ${event.request.url}`);
});

Ответ на запрос

Когда запрос поступает в ваш сервис-воркер, вы можете сделать два действия: проигнорировать его, пропустив его в сеть, или ответить на него. Ответ на запросы из сервис-воркера позволяет вам выбирать, что и как будет возвращено в ваш PWA, даже если пользователь находится офлайн.

Чтобы ответить на входящий запрос, вызовите event.respondWith() из обработчика событий fetch , например:

// fetch event handler in your service worker file
self.addEventListener("fetch", event => {
    const response = .... // a response or a Promise of response
    event.respondWith(response);
});

respondWith() необходимо вызывать синхронно и возвращать объект Response . Однако respondWith() нельзя вызвать после завершения обработки события fetch, как в асинхронном вызове. Если нужно дождаться полного ответа, можно передать методу respondWith() обещание, которое возвращает объект Response.

Создание ответов

Благодаря Fetch API вы можете создавать HTTP-ответы в своем коде JavaScript, и эти ответы можно кэшировать с помощью Cache Storage API и возвращать так, как если бы они поступали с веб-сервера.

Чтобы создать ответ, создайте новый объект Response , задав его тело и такие параметры, как статус и заголовки:

const simpleResponse = new Response("Body of the HTTP response");

const options = {
   status: 200,
   headers: {
    'Content-type': 'text/html'
   }
};
const htmlResponse = new Response("<b>HTML</b> content", options)

Ответ из кэша

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

Вы можете использовать API кэширования, чтобы проверить, доступен ли запрос, полученный от PWA, в кэше, и, если да, отправить его в ответ на respondWith() . Для этого сначала необходимо выполнить поиск в кэше. Функция match() , доступная в интерфейсе caches верхнего уровня, выполняет поиск по всем хранилищам в вашем источнике или по одному открытому объекту кэша.

Функция match() получает HTTP-запрос или URL в качестве аргумента и возвращает обещание, которое разрешается с помощью ответа, связанного с соответствующим ключом.

// Global search on all caches in the current origin
caches.match(urlOrRequest).then(response => {
   console.log(response ? response : "It's not in the cache");
});

// Cache-specific search
caches.open("pwa-assets").then(cache => {
  cache.match(urlOrRequest).then(response => {
    console.log(response ? response : "It's not in the cache");
  });
});

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

Обслуживание файлов только из кэша браузера подходит не для всех случаев. Например, пользователь или браузер могут очистить кэш. Поэтому вам следует определить собственные стратегии доставки ресурсов для вашего PWA. Вы не ограничены одной стратегией кэширования. Вы можете определить разные стратегии для разных шаблонов URL. Например, можно использовать одну стратегию для минимальных ресурсов пользовательского интерфейса, другую — для вызовов API и третью — для URL-адресов изображений и данных. Для этого прочитайте event.request.url в ServiceWorkerGlobalScope.onfetch и проанализируйте его с помощью регулярных выражений или шаблона URL . (На момент написания статьи шаблон URL поддерживался не на всех платформах).

Наиболее распространенные стратегии:

Кэш сначала
Сначала ищет кэшированный ответ и возвращается к сети, если ответ не найден.
Сеть прежде всего
Сначала запрашивает ответ из сети и, если ответ не получен, проверяет наличие ответа в кэше.
Устаревший при повторной проверке
Отправляет ответ из кэша, а в фоновом режиме запрашивает последнюю версию и сохраняет ее в кэше для следующего запроса актива.
Только сеть
Всегда отвечает ответом от сети или выдаёт ошибки. Кэш никогда не обращается.
Только кэш
Всегда отвечает ответом из кэша, иначе выдаёт ошибки. Сеть никогда не будет опрашиваться. Ресурсы, которые будут обслуживаться с использованием этой стратегии, должны быть добавлены в кэш до их запроса.

Кэш сначала

Используя эту стратегию, сервис-воркер ищет соответствующий запрос в кэше и возвращает соответствующий ответ, если он есть в кэше. В противном случае он извлекает ответ из сети (при необходимости обновляя кэш для будущих вызовов). Если нет ни ответа из кэша, ни ответа из сети, запрос завершается ошибкой. Поскольку обслуживание ресурсов без обращения к сети, как правило, происходит быстрее, эта стратегия отдаёт приоритет производительности, а не актуальности данных.

Стратегия Cache First

self.addEventListener("fetch", event => {
   event.respondWith(
     caches.match(event.request)
     .then(cachedResponse => {
       // It can update the cache to serve updated content on the next request
         return cachedResponse || fetch(event.request);
     }
   )
  )
});

Сначала сеть

Эта стратегия является зеркалом стратегии Cache First: она проверяет, может ли запрос быть выполнен из сети, и, если это невозможно, пытается получить его из кэша. Аналогично стратегии Cache First. Если нет ни ответа от сети, ни ответа из кэша, запрос завершается ошибкой. Получение ответа из сети обычно медленнее, чем из кэша, поскольку эта стратегия отдаёт приоритет обновлению контента, а не производительности.

Стратегия «Сеть прежде всего»

self.addEventListener("fetch", event => {
   event.respondWith(
     fetch(event.request)
     .catch(error => {
       return caches.match(event.request) ;
     })
   );
});

Устаревание при повторной проверке

Стратегия stale while revalidate немедленно возвращает кэшированный ответ, а затем проверяет сеть на наличие обновления, заменяя кэшированный ответ, если оно найдено. Эта стратегия всегда отправляет сетевой запрос, поскольку даже при обнаружении кэшированного ресурса она попытается обновить содержимое кэша данными, полученными из сети, чтобы использовать обновлённую версию в следующем запросе. Таким образом, эта стратегия позволяет воспользоваться преимуществами быстрой обработки данных, предлагаемых стратегией «сначала кэш», и обновлять кэш в фоновом режиме.

Устаревшая стратегия с повторной проверкой

self.addEventListener('fetch', event => {
  event.respondWith(
    caches.match(event.request).then(cachedResponse => {
        const networkFetch = fetch(event.request).then(response => {
          // update the cache with a clone of the network response
          const responseClone = response.clone()
          caches.open(url.searchParams.get('name')).then(cache => {
            cache.put(event.request, responseClone)
          })
          return response
        }).catch(function (reason) {
          console.error('ServiceWorker fetch failed: ', reason)
        })
        // prioritize cached response over network
        return cachedResponse || networkFetch
      }
    )
  )
})

Только сеть

Стратегия «только сеть» аналогична поведению браузеров без сервис-воркера или API Cache Storage. Запросы будут возвращать ресурс только в том случае, если его можно получить из сети. Это часто полезно для таких ресурсов, как запросы API, работающие только в режиме онлайн.

Стратегия «Только сеть»

Только кэш

Стратегия «Только кэш» гарантирует, что запросы никогда не будут отправлены в сеть; на все входящие запросы отвечает предварительно заполненный элемент кэша. Следующий код использует обработчик событий fetch с методом match хранилища кэша, чтобы отвечать только кэшу:

self.addEventListener("fetch", event => {
   event.respondWith(caches.match(event.request));
});

Стратегия «Только кэш».

Индивидуальные стратегии

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

Например, можно использовать стратегию «Сначала сеть» с тайм-аутом для приоритизации обновлённого контента, но только если ответ появляется в пределах заданного вами порогового значения. Вы также можете объединить кэшированный ответ с сетевым ответом и сформировать сложный ответ от сервис-воркера.

Обновление активов

Поддержание актуальности кэшированных ресурсов вашего PWA может быть непростой задачей. Хотя стратегия «замедлить с повторной проверкой» — один из способов решения этой задачи, она не единственная. В главе «Обновление» вы узнаете о различных методах поддержания актуальности контента и ресурсов вашего приложения.

Ресурсы