Мгновенная обработка обновлений Service Worker

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

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

Код для размещения на вашей странице

Следующий код выполняется во встроенном элементе <script> с использованием модулей JavaScript, импортированных из версии workbox-window размещенной в CDN. Он регистрирует сервис-воркера с помощью workbox-window и реагирует, если сервис-воркер застревает на этапе ожидания. Когда ожидающий сервис-воркер найден, этот код информирует пользователя о том, что доступна обновленная версия сайта, и предлагает перезагрузить его.

<!-- This script tag uses JavaScript modules, so the proper `type` attribute value is required -->
<script type="module">
  // This code sample uses features introduced in Workbox v6.
  import {Workbox} from 'https://storage.googleapis.com/workbox-cdn/releases/6.4.1/workbox-window.prod.mjs';

  if ('serviceWorker' in navigator) {
    const wb = new Workbox('/sw.js');
    let registration;

    const showSkipWaitingPrompt = async (event) => {
      // Assuming the user accepted the update, set up a listener
      // that will reload the page as soon as the previously waiting
      // service worker has taken control.
      wb.addEventListener('controlling', () => {
        // At this point, reloading will ensure that the current
        // tab is loaded under the control of the new service worker.
        // Depending on your web app, you may want to auto-save or
        // persist transient state before triggering the reload.
        window.location.reload();
      });

      // When `event.wasWaitingBeforeRegister` is true, a previously
      // updated service worker is still waiting.
      // You may want to customize the UI prompt accordingly.

      // This code assumes your app has a promptForUpdate() method,
      // which returns true if the user wants to update.
      // Implementing this is app-specific; some examples are:
      // https://open-ui.org/components/alert.research or
      // https://open-ui.org/components/toast.research
      const updateAccepted = await promptForUpdate();

      if (updateAccepted) {
        wb.messageSkipWaiting();
      }
    };

    // Add an event listener to detect when the registered
    // service worker has installed but is waiting to activate.
    wb.addEventListener('waiting', (event) => {
      showSkipWaitingPrompt(event);
    });

    wb.register();
  }
</script>

Если они соглашаются, messageSkipWaiting() сообщает ожидающему сервисному работнику вызвать self.skipWaiting() , что означает, что он начнет активироваться. После активации новый сервис-воркер возьмет на себя управление всеми существующими клиентами, вызывая controlling событие в workbox-window . Когда это происходит, текущая страница перезагружается с использованием последней версии всех предварительно кэшированных ресурсов и любой новой логики маршрутизации, обнаруженной в обновленном сервисном работнике.

Код, который нужно добавить в вашего сервис-воркера

После того, как вы получите код из предыдущего раздела на своей странице, вам нужно будет добавить в сервис-воркер код, который сообщит ему, когда следует пропустить фазу ожидания. Если вы generateSW из workbox-build и для параметра skipWaiting установлено значение false (по умолчанию), то все готово, поскольку приведенный ниже код будет автоматически включен в созданный вами рабочий файл службы.

Если вы пишете свой собственный сервис-воркер (возможно, в сочетании с одним из инструментов сборки Workbox в режиме injectManifest ) , вам необходимо самостоятельно добавить следующий код:

addEventListener('message', (event) => {
  if (event.data && event.data.type === 'SKIP_WAITING') {
    self.skipWaiting();
  }
});

Он будет прослушивать сообщения, отправленные сервисному работнику из workbox-window со значением type SKIP_WAITING , и когда это произойдет, вызывает self.skipWaiting() . За отправку этого сообщения отвечает метод messageSkipWaiting() в workbox-window , показанный в предыдущем примере кода.

Вам нужно показать подсказку?

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

  • Вы широко используете прекэширование. Что касается статических ресурсов, то позже у вас могут возникнуть проблемы, если вы используете стратегию «сначала сеть» или «только сеть» для навигационных запросов, но загружаете статические ресурсы с отложенной загрузкой. Это может привести к ситуациям, когда активы с версиями могут измениться, а работник службы не выполнил их предварительное кэширование. Предложение кнопки перезагрузки здесь может помочь избежать непредвиденных ситуаций.
  • Если вы обслуживаете предварительно кэшированный HTML. В этом случае вам следует настоятельно рассмотреть возможность добавления кнопки перезагрузки в обновлениях сервис-воркера, поскольку обновления этого HTML-кода не будут распознаваться до тех пор, пока обновленный сервис-воркер не возьмет на себя управление.
  • Если вы не полагаетесь в основном на кэширование во время выполнения. При кэшировании ресурсов во время выполнения вам не нужно сообщать пользователю о необходимости перезагрузки. По мере изменения активов с версиями они будут со временем добавляться в кэш времени выполнения — при условии, что запросы навигации используют стратегию «сначала сеть» или «только сеть».
  • При использовании стратегии «устаревшие во время повторной проверки» вы можете рассмотреть возможность использования модуля workbox-broadcast-update для уведомления пользователей об обновлениях Service Worker.

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