Comment puis-je demander un client lors de l'initialisation d'un ServiceWorker pour éviter de recharger la page?

J'ai du mal à m'emballer sur l' API Clients.claim du ServiceWorker. D'après ce que je comprends ( ici et ici ), je peux appeler claim() sur l'acteur de service actif pour éviter d'avoir à actualiser la page pour initialiser le ServiceWorker. Je ne peux pas le faire fonctionner et finis toujours par être rafraîchissant. Voici mon code:

À l'intérieur du travailleur du service:

 self.addEventListener('install', function (event) { self.skipWaiting(); event.waitUntil(caches.open(CURRENT_CACHE_DICT.prefetch) .then(function(cache) { var cachePromises = PREFETCH_URL_LIST.map(function(prefetch_url) { var url = new URL(prefetch_url, location.href), request = new Request(url, {mode: 'no-cors'}); return fetch(request).then(function(response) { if (response.status >= 400) { throw new Error('request for ' + prefetch_url + ' failed with status ' + response.statusText); } return cache.put(prefetch_url, response); }).catch(function(error) { console.error('Not caching ' + prefetch_url + ' due to ' + error); }); }); return Promise.all(cachePromises).then(function() { console.log('Pre-fetching complete.'); }); }).catch(function(error) { console.error('Pre-fetching failed:', error); }) ); }); self.addEventListener('activate', function (event) { // claim the scope immediately // XXX does not work? //self.clients.claim(); event.waitUntil(self.clients.claim() .then(caches.keys) .then(function(cache_name_list) { return Promise.all( cache_name_list.map(function() {...} ); }) ); }); 

Ce qui précède s'exécute, mais je finis par être rafraîchissant et j'ai trouvé une erreur d' Illegal invocation dans les composants internes de Chrome ServiceWorker. Si je supprime les clients.claim au gestionnaire waitUntil et décommentez le précédent, je n'obtiens aucune erreur, mais je dois encore rafraîchir. Le débogueur affiche:

 Console: {"lineNumber":128,"message":"Pre-fetching complete.","message_level":1,"sourceIdentifier":3,"sourceURL":""} Console: {"lineNumber":0,"message":"Uncaught (in promise) TypeError: Illegal invocation","message_level":3,"sourceIdentifier":1,"sourceURL":""} 

Le rafraîchissement est déclenché comme ceci:

 function waitForInstallation(registration) { return new RSVP.Promise(function(resolve, reject) { if (registration.installing) { registration.installing.addEventListener('statechange', function(e) { if (e.target.state == 'installed') { resolve(); } else if (e.target.state == 'redundant') { reject(e); } }); } else { resolve(); } }); } // refreshing should not be necessary if scope is claimed on activate function claimScope(installation) { return new RSVP.Promise(function (resolve, reject) { if (navigator.serviceWorker.controller) { resolve(); } else { reject(new Error("Please refresh to initialize serviceworker.")); } }); } rJS(window) .declareMethod('render', function (my_option_dict) { var gadget = this; if ('serviceWorker' in navigator) { return new RSVP.Queue() .push(function () { return navigator.serviceWorker.register( my_option_dict.serviceworker_url, {scope: my_option_dict.scope} ); }) .push(function (registration) { return waitForInstallation(registration); }) .push(function (installation) { return claimScope(installation); }) .push(null, function (my_error) { console.log(my_error); throw my_error; }); } else { throw new Error("Browser does not support serviceworker."); } }); 

Question :
Comment puis-je empêcher correctement la page d'être actualisé pour activer le ServiceWorker en utilisant une claim ? Aucun des liens que j'ai trouvé mentionnés n'a de vérifier explicitement le controller mais je suppose que si un ServiceWorker est actif, il aurait un contrôleur accessible.

Merci d'avoir dégagé des informations.

MODIFIER:
J'ai découvert avec l'aide d'en bas. Cela m'a fait fonctionner:

 // runs while an existing worker runs or nothing controls the page (update here) self.addEventListener('install', function (event) { event.waitUntil(caches.open(CURRENT_CACHE_DICT.dictionary) .then(function(cache) { var cache_promise_list = DICTIONARY_URL_LIST.map(function(prefetch_url) {...}); return Promise.all(cache_promise_list).then(function() { console.log('Pre-fetching complete.'); }); }) .then(function () { // force waiting worker to become active worker (claim) self.skipWaiting(); }).catch(function(error) { console.error('Pre-fetching failed:', error); }) ); }); // runs active page, changes here (like deleting old cache) breaks page self.addEventListener('activate', function (event) { event.waitUntil(caches.keys() .then(function(cache_name_list) { return Promise.all( cache_name_list.map(function(cache_name) { ... }) ); }) .then(function () { return self.clients.claim(); }) ); }); 

Script de déclenchement:

 var SW = navigator.serviceWorker; function installServiceWorker(my_option_dict) { return new RSVP.Queue() .push(function () { return SW.getRegistration(); }) .push(function (is_registered_worker) { // XXX What if this isn't mine? if (!is_registered_worker) { return SW.register( my_option_dict.serviceworker_url, { "scope": my_option_dict.scope } ); } return is_registered_worker; }); } function waitForInstallation(registration) { return new RSVP.Promise(function(resolve, reject) { if (registration.installing) { // If the current registration represents the "installing" service // worker, then wait until the installation step completes (during // which any defined resources are pre-fetched) to continue. registration.installing.addEventListener('statechange', function(e) { if (e.target.state == 'installed') { resolve(registration); } else if (e.target.state == 'redundant') { reject(e); } }); } else { // Otherwise, if this isn't the "installing" service worker, then // installation must have beencompleted during a previous visit to this // page, and the any resources will already have benn pre-fetched So // we can proceed right away. resolve(registration); } }); } // refreshing should not be necessary if scope is claimed on activate function claimScope(registration) { return new RSVP.Promise(function (resolve, reject) { if (registration.active.state === 'activated') { resolve(); } else { reject(new Error("Please refresh to initialize serviceworker.")); } }); } rJS(window) .ready(function (my_gadget) { my_gadget.property_dict = {}; }) .declareMethod('render', function (my_option_dict) { var gadget = this; if (!SW) { throw new Error("Browser does not support serviceworker."); } return new RSVP.Queue() .push(function () { return installServiceWorker(my_option_dict), }) .push(function (my_promise) { return waitForInstallation(my_promise); }) .push(function (my_installation) { return claimScope(my_installation); }) .push(function () { return gadget; }) .push(null, function (my_error) { console.log(my_error); throw my_error; }); }); 

Tout d'abord, vous semblez obtenir l'erreur en raison d'une erreur de frappe dans votre code. Voir les notes à ce sujet en bas.

Par ailleurs, skipWaiting() et Clients.claim() installe et active le nouveau SW avec une requête singe. Mais tout naturellement, vous n'obtiendrez que des actifs statiques comme css après votre rechargement.

Donc, même s'il est équipé de skipWaiting() et Clients.claim() , vous avez besoin de deux recharges de page pour voir static contenu static mis à jour tel que le nouveau html ou les styles;

Charge de la page # 1

  • La demande de sw.js est faite, et comme le contenu de SW est modifié, l' événement d' install est déclenché.
  • L' activate événement est également déclenchée, car vous avez self.skipWaiting() dans votre gestionnaire d' install .
  • Par conséquent, votre gestionnaire d' activate fonctionne et il y a votre appel self.clients.claim() . Ce qui ordonnera au SW de prendre le contrôle de tous les clients qui sont sous le contrôle de son prédécesseur.
  • À ce stade, les actifs dans le cache sont mis à jour et vos pages sont toutes contrôlées par un nouveau serviceleur. Toute requête Ajax dans la gamme de travailleurs du service renverra les réponses nouvellement mises en cache, par exemple.

Charge de la page # 2

Votre application fonctionne et votre SW répond du cache en détournant les requêtes comme d'habitude. Mais maintenant, les caches sont à jour, et l'utilisateur peut utiliser complètement l' application avec de nouveaux atouts.

L'erreur que vous obtenez

Uncaught (in promise) TypeError: Illegal invocation erreur d' Uncaught (in promise) TypeError: Illegal invocation doit être due à une parenthèse manquante dans votre gestionnaire d' activate ;

  event.waitUntil(self.clients.claim() .then(caches.keys) .then(function(cache_name_list) { return Promise.all( cache_name_list.map(function() {...} ); <-- Here is our very lonely single parenthesis. }) ); 

Cette erreur devrait disparaître si vous la réparez.