Использование служебных рабочих ролей для управления сетевыми запросами

Служебные рабочие роли — это особый тип веб-рабочих ролей с возможностью перехвата, изменения и реагирования на сетевые Fetch запросы с помощью API. Служебные рабочие роли могут обращаться Cache к API и асинхронным клиентским хранилищам данных, таким как IndexedDB, для хранения ресурсов.

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

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

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

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

Регистрация рабочей роли службы

Как и другие веб-рабочие роли, рабочие роли обслуживания должны существовать в отдельном файле. Ссылка на этот файл выполняется при регистрации рабочей роли службы, как показано в следующем коде:

if ("serviceWorker" in navigator) {
    navigator.serviceWorker.register("/serviceworker.js");
}

Веб-браузер, в котором выполняется PWA, может предоставлять различные уровни поддержки для служебных рабочих ролей. Кроме того, контекст, в котором выполняется PWA, может быть небезопасн. Поэтому рекомендуется проверить наличие объекта перед выполнением кода, связанного navigator.serviceWorker с служебной рабочей ролью. В приведенном выше коде рабочая роль службы регистрируется с помощью serviceworker.js файла, расположенного в корне сайта.

Поместите файл служебной роли в каталог самого высокого уровня, которым он будет управлять. Такой каталог называется область рабочей роли службы. В предыдущем коде файл хранится в корневом каталоге приложения, а рабочая роль службы управляет всеми страницами, которые находятся под доменным именем приложения.

Если файл рабочей роли службы хранился в js каталоге, область рабочей роли службы будет ограничен каталогом js и любыми подкаталогами. Рекомендуется поместить файл рабочей роли службы в корень приложения, если вам не нужно уменьшить область рабочей роли службы.

Перехват запросов

Событие main, используемое в рабочей роли службы, — это fetch событие. Событие fetch запускается каждый раз, когда браузер, запущенный приложением, пытается получить доступ к содержимому в область рабочей роли службы.

В следующем коде показано, как добавить прослушиватель для fetch события:

self.addEventListener("fetch", event => {
  console.log('WORKER: Fetching', event.request);
});

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

Вот несколько примеров того, что можно сделать в обработчике fetch :

  • Если доступно, верните ответ из кэша; В противном случае можно вернуться к запросу ресурса по сети.
  • Извлеките ресурс из сети, кэшируйте копию и верните ответ.
  • Разрешить пользователям указать предпочтения для сохранения данных.
  • Предоставьте образ-заполнитель для определенных запросов изображений.
  • Создайте ответ непосредственно в рабочей роли службы.

Жизненный цикл рабочей роли службы

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

  1. Зарегистрируйте рабочую роль службы.

  2. Браузер скачивает файл JavaScript, устанавливает рабочую роль службы и активирует install событие. Событие можно использовать для предварительного install кэширования любых важных и долгоживующих файлов (таких как CSS-файлы, файлы JavaScript, изображения логотипов или автономные страницы) из приложения.

    self.addEventListener("install", event => {
        console.log("WORKER: install event in progress.");
    });
    
  3. Активируется рабочая роль службы, которая активирует activate событие. Используйте это событие для очистки устаревших кэшей.

    self.addEventListener("activate", event => {
        console.log("WORKER: activate event in progress.");
    });
    
  4. Рабочая роль службы готова к запуску при обновлении страницы или при переходе пользователя на новую страницу сайта. Если вы хотите запустить рабочую роль службы без ожидания, используйте self.skipWaiting() метод во время install события следующим образом:

    self.addEventListener("install", event => {
        self.skipWaiting();
        // …
    });
    
  5. Рабочая роль службы теперь запущена и может прослушивать fetch события.

Ресурсы для предварительного кэширования

Когда пользователь обращается к приложению в первый раз, устанавливается рабочая роль службы приложения. install Используйте событие в рабочей роли службы, чтобы определить, когда это происходит, и кэшировать все статические ресурсы, необходимые приложению. Кэширование статических ресурсов приложения (таких как HTML, CSS и код JavaScript), необходимых начальной странице, позволяет приложению запускаться, даже если устройство пользователя находится в автономном режиме.

Чтобы кэшировать ресурсы приложения, используйте глобальный caches объект и cache.addAll метод, как показано ниже:

// The name of the cache your app uses.
const CACHE_NAME = "my-app-cache";
// The list of static files your app needs to start.
const PRE_CACHED_RESOURCES = ["/", "styles.css", "app.js"];

// Listen to the `install` event.
self.addEventListener("install", event => {
  async function preCacheResources() {
    // Open the app's cache.
    const cache = await caches.open(CACHE_NAME);
    // Cache all static resources.
    cache.addAll(PRE_CACHED_RESOURCES);
  }

  event.waitUntil(preCacheResources());
});

Обратите внимание, install что после начальной установки событие больше не запускается. Сведения об обновлении кода рабочей роли службы см. в статье Обновление рабочей роли службы.

Теперь можно использовать fetch событие для возврата статических ресурсов из кэша, а не для их повторной загрузки из сети:

self.addEventListener("fetch", event => {
  async function returnCachedResource() {
    // Open the app's cache.
    const cache = await caches.open(CACHE_NAME);
    // Find the response that was pre-cached during the `install` event.
    const cachedResponse = await cache.match(event.request.url);

    if (cachedResponse) {
      // Return the resource.
      return cachedResponse;
    } else {
      // The resource wasn't found in the cache, so fetch it from the network.
      const fetchResponse = await fetch(event.request.url);
      // Put the response in cache.
      cache.put(event.request.url, fetchResponse.clone());
      // And return the response.
      return fetchResponse.
    }
  }

  event.respondWith(returnCachedResource());
});

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

Использование настраиваемой автономной страницы

Если приложение использует несколько HTML-страниц, в автономном режиме обычно выполняется перенаправление запросов навигации по страницам на настраиваемую страницу ошибок, когда устройство пользователя находится в автономном режиме:

// The name of the cache your app uses.
const CACHE_NAME = "my-app-cache";
// The list of static files your app needs to start.
// Note the offline page in this list.
const PRE_CACHED_RESOURCES = ["/", "styles.css", "app.js", "/offline"];

// Listen to the `install` event.
self.addEventListener("install", event => {
  async function preCacheResources() {
    // Open the app's cache.
    const cache = await caches.open(CACHE_NAME);
    // Cache all static resources.
    cache.addAll(PRE_CACHED_RESOURCES);
  }

  event.waitUntil(preCacheResources());
});

self.addEventListener("fetch", event => {
  async function navigateOrDisplayOfflinePage() {
    try {
      // Try to load the page from the network.
      const networkResponse = await fetch(event.request);
      return networkResponse;
    } catch (error) {
      // The network call failed, the device is offline.
      const cache = await caches.open(CACHE_NAME);
      const cachedResponse = await cache.match("/offline");
      return cachedResponse;
    }
  }

  // Only call event.respondWith() if this is a navigation request
  // for an HTML page.
  if (event.request.mode === 'navigate') {
    event.respondWith(navigateOrDisplayOfflinePage());
  }
});

Обновление рабочей роли службы

Установка новой версии служебной рабочей роли

Если вы внесете изменения в код служебной рабочей роли и развернете новый файл рабочей роли службы на веб-сервере, устройства пользователей постепенно начнут использовать новую рабочую роль службы.

Каждый раз, когда пользователь переходит на одну из страниц приложения, браузер, в котором выполняется приложение, проверяет, доступна ли на сервере новая версия служебной рабочей роли. Браузер обнаруживает новые версии путем сравнения содержимого существующей рабочей роли службы и новой рабочей роли службы. При обнаружении изменения устанавливается новая рабочая роль службы (активируется ее install событие), а затем новая рабочая роль службы ожидает прекращения использования существующей рабочей роли службы на устройстве.

На практике это означает, что одновременно могут выполняться две служебные рабочие роли, но только один перехватывает сетевые запросы приложения. При закрытии приложения существующая рабочая роль службы перестает использоваться. При следующем открытии приложения активируется новая рабочая роль службы. Событие activate активируется, и новая рабочая роль службы начинает перехват событий fetch .

Вы можете принудительно активировать новую рабочую роль службы сразу после ее установки с помощью self.skipWaiting() обработчика событий служебной install рабочей роли.

Дополнительные сведения об обновлении рабочих ролей службы см. в статье Обновление рабочей роли службы на web.dev.

Обновление кэшированных статических файлов

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

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

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

Затем обновите код рабочей роли службы, чтобы кэшировать новые статические ресурсы во время install:

// The name of the new cache your app uses.
const CACHE_NAME = "my-app-cache-v2";
// The list of static files your app needs to start.
const PRE_CACHED_RESOURCES = ["/", "styles-124656.css", "app-576391.js"];

// Listen to the `install` event.
self.addEventListener("install", event => {
  async function preCacheResources() {
    // Open the app's cache.
    const cache = await caches.open(CACHE_NAME);
    // Cache the new static resources.
    cache.addAll(PRE_CACHED_RESOURCES);
  }

  event.waitUntil(preCacheResources());
});

// Listen to the `activate` event to clear old caches.
self.addEventListener("activate", event => {
  async function deleteOldCaches() {
    // List all caches by their names.
    const names = await caches.keys();
    await Promise.all(names.map(name => {
      if (name !== CACHE_NAME) {
        // If a cache's name is the current name, delete it.
        return caches.delete(name);
      }
    }));
  }

  event.waitUntil(deleteOldCaches());
});

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

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

Проверка сетевых подключений в PWA

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

Используйте следующие параметры для проверки сетевого подключения:

Свойство navigator.onLine является логическим, который позволяет узнать текущее состояние сети. Если значение равно true, пользователь находится в сети; в противном случае пользователь находится в автономном режиме.

Дополнительные сведения см. в разделе navigator.onLine в MDN.

События в сети и в автономном режиме

Вы можете выполнить действия при изменении сетевого подключения. Вы можете прослушивать события сети и принимать меры в ответ на события сети. События доступны в элементах window, documentи document.body , как показано ниже:

window.addEventListener("online",  function(){
    console.log("You are online!");
});
window.addEventListener("offline", function(){
    console.log("Oh no, you lost your network connection.");
});

Дополнительные сведения см. в разделе Navigator.onLine в MDN.

Другие возможности

Main обязанности работника службы заключается в том, чтобы сделать приложение быстрее и надежнее в случае нестабильного сетевого подключения. Рабочие роли служб в основном используют fetch события и Cache API для этого, но они могут использовать другие API для расширенных сценариев, таких как:

  • Фоновая синхронизация данных.
  • Периодическая синхронизация данных.
  • Загрузка больших фоновых файлов.
  • Обработка push-сообщений и уведомления о ней.

Фоновая синхронизация

Используйте API фоновой синхронизации, чтобы пользователи могли продолжать использовать ваше приложение и выполнять действия, даже если устройство пользователя находится в автономном режиме.

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

Дополнительные сведения см. в статье Использование API фоновой синхронизации для синхронизации данных с сервером.

Фоновая синхронизация периодов

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

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

Дополнительные сведения см. в статье Использование API периодической фоновой синхронизации для регулярного получения нового содержимого.

Загрузка больших фоновых файлов

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

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

Дополнительные сведения см. в статье Использование API фоновой выборки для получения больших файлов, когда приложение или рабочая роль службы не запущены.

Push-сообщения

Push-сообщения можно отправлять пользователям без необходимости использовать приложение в то время. Рабочая роль службы может прослушивать push-сообщения, отправляемые сервером, даже если приложение не запущено, и отображать уведомление в центре уведомлений операционной системы.

Дополнительные сведения см. в статье Повторное вовлечение пользователей с помощью push-сообщений.

Отладка с помощью средств разработки

С помощью средств разработки Microsoft Edge можно узнать, правильно ли зарегистрирована рабочая роль службы, а также определить, в каком состоянии жизненного цикла находится рабочая роль службы в данный момент. Кроме того, можно выполнить отладку кода JavaScript в рабочей роли службы.

Дополнительные сведения см. в разделе Отладка рабочей роли службы.

См. также