Передача сообщений между воркером и страницей

Service Worker в контексте Workbox является отдельным скриптом, который выполняется в отдельном потоке браузера, независимо от основной страницы. Это значит, что прямой доступ к DOM недоступен, и обмен данными осуществляется через механизм сообщений. В JavaScript для передачи информации между основной страницей и воркером используются события postMessage и слушатели message.

В Workbox для удобства работы с сообщениями можно использовать встроенный объект workbox.core.clientsClaim() и методы регистрации Service Worker, которые упрощают коммуникацию.

Отправка сообщений с страницы в Service Worker

Для передачи данных из страницы в воркер применяется метод postMessage. Пример базовой схемы:

if ('serviceWorker' in navigator) {
  navigator.serviceWorker.ready.then(registration => {
    registration.active.postMessage({
      type: 'CACHE_URL',
      payload: '/path/to/resource'
    });
  });
}

Ключевые моменты:

  • registration.active — текущий активный Service Worker.
  • Сообщение передается как объект, содержащий type и payload для структурирования данных.
  • Вызов navigator.serviceWorker.ready гарантирует, что воркер полностью активен и готов к приему сообщений.

Обработка сообщений в Service Worker

В Service Worker устанавливается слушатель события message для обработки входящих сообщений:

self.addEventListener('message', (event) => {
  const { type, payload } = event.data;
  switch(type) {
    case 'CACHE_URL':
      caches.open('dynamic-cache').then(cache => cache.add(payload));
      break;
    default:
      console.log('Неизвестный тип сообщения:', type);
  }
});

Особенности обработки:

  • event.data содержит переданные с страницы данные.
  • Использование switch или if для разных типов сообщений позволяет строить масштабируемую систему команд.
  • Асинхронные операции, такие как добавление в кэш, требуют использования промисов, чтобы корректно завершить задачу.

Ответ от Service Worker к странице

Service Worker может отправлять данные обратно с использованием объекта clients:

self.clients.matchAll().then(clients => {
  clients.forEach(client => {
    client.postMessage({
      type: 'CACHE_COMPLETE',
      payload: { url: '/path/to/resource' }
    });
  });
});

Важные моменты:

  • self.clients.matchAll() возвращает список всех контролируемых клиентов (вкладок, окон) текущего Service Worker.
  • Каждому клиенту вызывается postMessage, что позволяет уведомлять сразу несколько страниц.
  • Структурирование сообщений аналогично отправке с страницы: обязательно указывать type для различения событий.

Прием сообщений на странице

Страница подписывается на сообщения от Service Worker через объект navigator.serviceWorker:

navigator.serviceWorker.addEventListener('message', event => {
  const { type, payload } = event.data;
  switch(type) {
    case 'CACHE_COMPLETE':
      console.log('Ресурс закеширован:', payload.url);
      break;
    default:
      console.log('Неизвестный тип сообщения от воркера:', type);
  }
});

Особенности:

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

Использование Workbox для упрощения обмена данными

Workbox предоставляет вспомогательные методы для работы с событиями message и кэшированием. Например, workbox.routing можно совместить с передачей команд:

import { registerRoute } from 'workbox-routing';
import { CacheFirst } from 'workbox-strategies';

registerRoute(
  ({ request }) => request.destination === 'image',
  new CacheFirst({
    cacheName: 'images-cache'
  })
);

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

Пояснения:

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

Передача сложных данных

Для передачи объектов с массивами, буферами или бинарными данными используется StructuredClone алгоритм, встроенный в postMessage. Он позволяет передавать практически любые JavaScript-объекты:

navigator.serviceWorker.controller.postMessage({
  type: 'UPLOAD_DATA',
  payload: { buffer: new Uint8Array([1,2,3,4]) }
});

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

Рекомендации по архитектуре обмена сообщениями

  • Разделение по типам сообщений — каждый тип команды должен обрабатываться отдельно для упрощения поддержки.
  • Использование промисов в обработчиках сообщений — гарантирует корректное завершение асинхронных операций.
  • Логирование ошибок — все неизвестные сообщения или исключения должны фиксироваться для отладки.
  • Контроль версий — при обновлении Service Worker важно предусматривать механизм уведомления клиентов о смене воркера через сообщения.

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