Ручное управление обновлением через postMessage

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


Основы механизма postMessage

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

// В клиентском коде
navigator.serviceWorker.controller.postMessage({
  action: 'skipWaiting'
});

На стороне сервис-воркера необходимо обработать это сообщение:

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

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

  • navigator.serviceWorker.controller существует только если активен сервис-воркер для данной страницы.
  • postMessage позволяет передавать объекты с произвольными данными.
  • Метод skipWaiting() позволяет сервис-воркеру сразу активироваться, минуя стандартное ожидание завершения всех открытых вкладок.

Интеграция с sw-precache

При генерации service-worker.js через sw-precache можно включить обработку сообщений для контроля обновления:

importScripts('sw-toolbox.js');

self.addEventListener('message', function(event) {
  if (event.data && event.data.action === 'skipWaiting') {
    self.skipWaiting();
  }
});

Сгенерированный service-worker.js автоматически кэширует ресурсы, а добавленный блок для postMessage обеспечивает мгновенное обновление без перезагрузки всех клиентов.


Использование ручного обновления на клиенте

  1. Отслеживание новой версии сервис-воркера
navigator.serviceWorker.register('/service-worker.js').then(registration => {
  registration.onupdatefo und = () => {
    const newWorker = registration.installing;

    newWorker.onstatecha nge = () => {
      if (newWorker.state === 'installed' && navigator.serviceWorker.controller) {
        // Новый сервис-воркер установлен и готов к активации
        console.log('Новая версия доступна');
      }
    };
  };
});
  1. Инициирование активации через postMessage
function activateNewWorker() {
  navigator.serviceWorker.getRegistration().then(reg => {
    if (reg.waiting) {
      reg.waiting.postMessage({ action: 'skipWaiting' });
    }
  });
}

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

  • reg.waiting содержит сервис-воркер, который завершил установку, но ожидает активации.
  • Вызов skipWaiting гарантирует, что новый воркер станет активным, даже если вкладки с предыдущей версией открыты.

Обработка обновлений после активации

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

navigator.serviceWorker.addEventListener('controllerchange', () => {
  window.location.reload();
});
  • Событие controllerchange срабатывает, когда новый сервис-воркер начинает контролировать страницу.
  • Чаще всего используется перезагрузка страницы, чтобы клиент начал использовать обновленные ресурсы из кэша.

Расширенные сценарии

  1. Уведомление пользователя о новой версии
if (newWorker.state === 'installed') {
  showUpdateBanner(() => {
    newWorker.postMessage({ action: 'skipWaiting' });
  });
}
  • Позволяет дать пользователю контроль над обновлением.
  • Можно реализовать «Кнопку обновить», которая инициирует postMessage.
  1. Множественные вкладки

При работе с несколькими вкладками важно синхронизировать активацию сервис-воркера. Один из подходов — использовать BroadcastChannel для уведомления всех вкладок об обновлении:

const channel = new BroadcastChannel('sw-updates');
channel.postMessage('update-available');

channel.onmess age = event => {
  if (event.data === 'update-available') {
    showUpdateBanner();
  }
};

Рекомендации по безопасности и стабильности

  • Всегда проверять наличие navigator.serviceWorker.controller перед вызовом postMessage.
  • Обрабатывать любые сообщения на стороне сервис-воркера с проверкой структуры данных.
  • Не использовать skipWaiting автоматически без уведомления пользователя, если обновление может нарушить текущую работу приложения.
  • Логировать состояния сервис-воркера для упрощения отладки.

Итоговый поток работы

  1. Клиент регистрирует сервис-воркер.
  2. sw-precache кэширует ресурсы.
  3. Обнаружение новой версии воркера через onupdatefound.
  4. Клиент отправляет postMessage с skipWaiting.
  5. Новый воркер активируется.
  6. Событие controllerchange инициирует обновление страницы или другие действия.

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