How: ServiceWorker проверяет, готов ли к обновлению

То, что я пытаюсь достичь:

  • показать страницу с загрузчиком /spinner

  • если service-worker.js зарегистрирован и активен, затем проверьте наличие обновлений

    • , если нет обновлений, а затем удалить загрузчик
    • если updatefound и установлена ​​новая версия, затем перезагрузите страницу
  • else register service-worker.js
    • когда updatefound, что означает, что новый был установлен, удалить загрузчик

Я использую модуль sw-precache для генерации service-worker.js и следующего регистрационного кода:

window.addEventListener('load', function() {

  // show loader
  addLoader();

  navigator.serviceWorker.register('service-worker.js')
    .then(function(swRegistration) {

      // react to changes in `service-worker.js`
      swRegistration.onupdatefound = function() {
        var installingWorker = swRegistration.installing;
        installingWorker.onstatechange = function() {
          if(installingWorker.state === 'installed' && navigator.serviceWorker.controller){

            // updated content installed
            window.location.reload();
          } else if (installingWorker.state === 'installed'){

            // new sw registered and content cached
            removeLoader();
          }
        };
      }

      if(swRegistration.active){
        // here I know that `service-worker.js` was already installed
        // but not sure it there are changes
        // If there are no changes it is the last thing I can check
        // AFAIK no events are fired afterwards
      }

    })
    .catch(function(e) {
      console.error('Error during service worker registration:', e);
    });
});

После прочтения spec ясно, что обработчиков нет, например, updatenotfound. Похоже, что serviceWorker.register проверяет, изменилось ли service-worker.js внутренне, запустив get-newest-worker-algorithm, но я не вижу похожих методов, открытых через public api.

Я думаю, что мои варианты:

  • подождите пару секунд после того, как регистрация рабочего рабочего активируется, чтобы узнать, запущен ли onupdatefound
  • выполнить пожарные пользовательские события из кода service-worker.js, если кеш не был обновлен

Любые другие предложения?


Edit:

Я придумал код, который решает эту проблему, используя postMessage() между регистрацией SW и клиентом SW (как предлагалось @pate)

После демонстрации пытается выполнить проверки через postMessage между клиентом SW и регистрацией SW, но не работает, поскольку код SW уже кэшируется DEMO


Edit:

Итак, похоже, что я не могу реализовать то, что хочу, потому что:

  • Когда рабочий сервисный активен, вы не можете проверять наличие обновлений, оценивая некоторый код в SW - это все тот же кешированный SW, никаких изменений там
  • вам нужно подождать onupdatefound, больше ничего не будет уведомлять об изменениях в SW
  • activation старшего ПО перед onupdatefound
  • Если нет изменений, ничего не срабатывает после activation
  • Регистрация SW update() незрелая, продолжает меняться, Starting with Chrome 46, update() returns a promise that resolves with 'undefined' if the operation completed successfully or there was no update
  • Установка таймаута для отложенного рендеринга представления является субоптимальной, поскольку нет четкого ответа на то, как долго он должен быть установлен, и зависит от размера SW.

Ответы

Ответ 1

Другой ответ, предоставленный Фабио, не работает. Сервисный рабочий script не имеет доступа к DOM. Невозможно удалить что-либо из DOM или, например, манипулировать любыми данными, которые обрабатывают элементы DOM изнутри Service Worker. Это script выполняется отдельно без общего состояния.

Однако вы можете отправлять сообщения между фактическим JS-страницей и Работодателем службы. Я не уверен, что это лучший способ сделать то, что просит ОП, но может быть использовано для его достижения.

  • Зарегистрируйте обработчик onmessage на странице
  • Отправить сообщение из события активации или установки SW на страницу
  • Действуйте соответственно, когда сообщение получено на странице

Я сам сохранил номер версии SW в переменной внутри SW. Затем мой SW разместил этот номер версии на странице, и страница сохранила его в localStorage браузера. В следующий раз, когда страница загружается, SW отправляет текущий номер версии на страницу, а обработчик onmessage сравнивает ее с текущим сохраненным номером версии. Если они не совпадают, тогда SW был обновлен до некоторого номера версии, который был включен в mssage. После этого я обновил копию localStorage номера версии и сделал это и то.

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

Надеюсь, я смог ясно объяснить свои мысли.

Ответ 2

Единственный способ, который появляется в моем сознании, - запустить загрузчик нормально, а затем удалить его в сервис-работнике в функции установки. Я попробую попробовать в вашем сервисе-worker.js:

self.addEventListener('install', function(e) {
  console.log('[ServiceWorker] Install');
  e.waitUntil(
    caches.open(cacheName).then(function(cache) {
      console.log('[ServiceWorker] Caching app shell');
      return cache.addAll(filesToCache);
    }).then(function() {
        ***removeLoader();***
        return self.skipWaiting();
    })
  );
});