Трансляция обновлений на страницы с работниками службы

Эндрю Гуан
Andrew Guan

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

  • Информирование страницы об установке новой версии сервис-воркера, чтобы на странице могла отображаться кнопка «Обновить для обновления» для пользователя, чтобы немедленно получить доступ к новым функциям.
  • Уведомление пользователя об изменении кэшированных данных, произошедшем на стороне работника службы, путем отображения индикации, например: «Приложение теперь готово к работе в автономном режиме» или «Доступна новая версия контента» .
Схема, показывающая работника службы, взаимодействующего со страницей для отправки обновления.

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

Производственные кейсы

Tinder

Tinder PWA использует workbox-window для прослушивания важных моментов жизненного цикла сервис-воркера со страницы («установлено», «контролируется» и «активировано»). Таким образом, когда в игру вступает новый сервисный работник, он показывает баннер «Доступно обновление» , чтобы он мог обновить PWA и получить доступ к новейшим функциям:

Скриншот функции «Доступно обновление» веб-приложения Tinder.
В Tinder PWA работник службы сообщает странице, что новая версия готова, и на странице отображается баннер «Доступно обновление».

Сквош

В Squoosh PWA , когда сервис-воркер кэширует все необходимые ресурсы для работы в автономном режиме, он отправляет на страницу сообщение с всплывающим сообщением «Готов к работе в автономном режиме», сообщая пользователю об этой функции:

Скриншот веб-приложения Squoosh, готового к работе в автономном режиме.
В Squoosh PWA сервис-воркер передает обновление на страницу, когда кеш готов, и на странице отображается всплывающее сообщение «Готов к работе в автономном режиме».

Использование Workbox

Прослушивание событий жизненного цикла сервисного работника

workbox-window предоставляет простой интерфейс для прослушивания важных событий жизненного цикла сервис-воркера . Под капотом библиотека использует клиентские API, такие как updatefound и statechange , и предоставляет прослушиватели событий более высокого уровня в объекте workbox-window , что упрощает пользователю использование этих событий.

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

const wb = new Workbox('/sw.js');

wb.addEventListener('installed', (event) => {
  if (event.isUpdate) {
    // Show "Update App" banner
  }
});

wb.register();

Информировать страницу об изменениях данных кэша

Пакет Workbox workbox-broadcast-update предоставляет стандартный способ уведомления оконных клиентов об обновлении кэшированного ответа. Чаще всего это используется вместе со стратегией StaleWhileRevalidate .

Чтобы транслировать обновления, добавьте broadcastUpdate.BroadcastUpdatePlugin к параметрам вашей стратегии на стороне сервис-воркера:

import {registerRoute} from 'workbox-routing';
import {StaleWhileRevalidate} from 'workbox-strategies';
import {BroadcastUpdatePlugin} from 'workbox-broadcast-update';

registerRoute(
  ({url}) => url.pathname.startsWith('/api/'),
  new StaleWhileRevalidate({
    plugins: [
      new BroadcastUpdatePlugin(),
    ],
  })
);

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

navigator.serviceWorker.addEventListener('message', async (event) => {
  // Optional: ensure the message came from workbox-broadcast-update
  if (event.data.meta === 'workbox-broadcast-update') {
    const {cacheName, updatedUrl} = event.data.payload;

    // Do something with cacheName and updatedUrl.
    // For example, get the cached content and update
    // the content on the page.
    const cache = await caches.open(cacheName);
    const updatedResponse = await cache.match(updatedUrl);
    const updatedText = await updatedResponse.text();
  }
});

Использование API браузера

Если функциональности, предоставляемой Workbox, недостаточно для ваших нужд, используйте следующие API браузера для реализации «широковещательных обновлений» :

API широковещательного канала

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

Чтобы сообщить странице об установке нового сервис-воркера, используйте следующий код:

// Create Broadcast Channel to send messages to the page
const broadcast = new BroadcastChannel('sw-update-channel');

self.addEventListener('install', function (event) {
  // Inform the page every time a new service worker is installed
  broadcast.postMessage({type: 'CRITICAL_SW_UPDATE'});
});

Страница прослушивает эти события, подписываясь на sw-update-channel :

// Create Broadcast Channel and listen to messages sent to it
const broadcast = new BroadcastChannel('sw-update-channel');

broadcast.onmessage = (event) => {
  if (event.data && event.data.type === 'CRITICAL_SW_UPDATE') {
    // Show "update to refresh" banner to the user.
  }
};

Это простой метод, но его ограничением является поддержка браузерами: на момент написания статьи Safari не поддерживает этот API .

Клиентский API

Клиентский API обеспечивает простой способ взаимодействия с несколькими клиентами из сервис-воркера путем перебора массива объектов Client .

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

// Obtain an array of Window client objects
self.clients.matchAll(options).then(function (clients) {
  if (clients && clients.length) {
    // Respond to last focused tab
    clients[0].postMessage({type: 'MSG_ID'});
  }
});

На странице реализован обработчик сообщений для перехвата этих сообщений:

// Listen to messages
navigator.serviceWorker.onmessage = (event) => {
     if (event.data && event.data.type === 'MSG_ID') {
         // Process response
   }
};

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

Канал сообщений

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

const messageChannel = new MessageChannel();

// Init port
navigator.serviceWorker.controller.postMessage({type: 'PORT_INITIALIZATION'}, [
  messageChannel.port2,
]);

Страница прослушивает сообщения, реализуя обработчик onmessage на этом порту:

// Listen to messages
messageChannel.port1.onmessage = (event) => {
  // Process message
};

Сервис-воркер получает порт и сохраняет ссылку на него:

// Initialize
let communicationPort;

self.addEventListener('message', (event) => {
  if (event.data && event.data.type === 'PORT_INITIALIZATION') {
    communicationPort = event.ports[0];
  }
});

С этого момента он может отправлять сообщения на страницу, вызывая postMessage() в ссылке на порт:

// Communicate
communicationPort.postMessage({type: 'MSG_ID' });

MessageChannel может быть более сложным в реализации из-за необходимости инициализации портов, но он поддерживается всеми основными браузерами .

Следующие шаги

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

Дополнительные шаблоны взаимодействия Window и Service Worker см.:

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

Дополнительные ресурсы