Мгновенная обработка обновлений 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.

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