web-dev-qa-db-fra.com

Le technicien de service met les fichiers en cache mais l'événement d'extraction n'est jamais déclenché

Je viens de tenter d'implémenter des techniciens de service pour mettre en cache certains fichiers JSON et d'autres actifs sur un site statique (exécuté sur localhost chrome Version 47.0.2526.73 (64 bits)). Utilisation de cache.addAll ( ) J'ai ajouté mes fichiers dans le cache et lorsque j'ouvre l'onglet des ressources dans Chrome et que je clique sur le stockage en cache, tous les fichiers sont répertoriés.

screenshot json files shown in cache

Le problème que je rencontre est que mon technicien de service est répertorié comme "activé" et "en cours d'exécution" dans chrome: // service-worker-internals cependant, je ne peux pas déterminer si le travailleur intercepte réellement les demandes et sert les fichiers mis en cache. J'ai ajouté l'écouteur d'événements et même lorsque je console l'enregistrement de l'événement dans l'instance d'outils de développement des employés de service, il n'atteint jamais le point d'arrêt:

this.addEventListener('install', function(event) {
  event.waitUntil(
    caches.open('v1').then(function(cache) {
      console.log(cache);
      return cache.addAll([
        '/json/0.json',
        '/json/1.json',
        '/json/3.json',
        '/json/4.json',
        '/json/5.json',
      ]);
    })
  );
});

this.addEventListener('fetch', function(event) {
  console.log(event);
  var response;
  event.respondWith(caches.match(event.request).catch(function() {
    return fetch(event.request);
  }).then(function(r) {
    response = r;
    caches.open('v1').then(function(cache) {
      cache.put(event.request, response);
    });
    return response.clone();
  }).catch(function() {
  }));
});

Fondamentalement, je passe en revue les choses exactement comme décrit dans l'intro des travailleurs du service HTML5, mais je suis presque sûr que mes actifs ne sont pas servis depuis le cache. J'ai remarqué que les actifs servis par un travailleur de service sont notés comme tels dans l'onglet réseau de devtools dans la colonne taille en indiquant `` de service workers ''.

Il semble que mon code ne soit pas différent des exemples, mais il ne frappe jamais l'événement fetch pour une raison quelconque. Contenu essentiel de mon code: https://Gist.github.com/srhise/c2099b347f68b958884d

42
srhise

Après avoir examiné votre Gist et votre question, je pense que votre problème est lié à la portée.

D'après ce que j'ai déterminé avec les travailleurs de service (au moins avec les fichiers statiques), le travailleur de service n'a qu'une portée maximale du répertoire dans lequel il se trouve. C'est-à-dire qu'il ne peut pas charger les fichiers/demandes/réponses qui sont extraits à partir d'un emplacement au niveau ou au-dessus de sa structure, uniquement en dessous.

Par exemple, /js/service-worker.js ne pourra charger que des fichiers dans/js/{dirName} /.

Par conséquent, si vous modifiez l'emplacement de votre technicien de service à la racine de votre projet Web, l'événement fetch doit se déclencher et vos actifs doivent se charger à partir du cache.

Donc quelque chose comme /service-worker.js, qui devrait pouvoir accéder au répertoire/json, car il est plus profond que le fichier service-worker.js.

Ceci est expliqué plus en détail ici, dans la section "Enregistrer un travailleur de service". https://developers.google.com/web/fundamentals/getting-started/primers/service-workers

62
Stephen Archer

Je me suis débattu avec cela pendant longtemps, et je pense que la documentation relative à la question fait gravement défaut. D'après mon expérience, il existe une distinction très importante:

Le technicien de service ne peut intercepter des événements d'extraction que s'il se trouve dans ou au-dessus de la portée de RL à partir de laquelle il est accessible.

Par exemple, mon fichier sw.js se trouvait à /static/sw.js. Lors de l'accès à la racine de mon site à / et tentant d'intercepter les événements de récupération dans les fichiers js dans /static/js/common.js, les événements d'extraction n'ont pas été interceptés, même si la portée de mon technicien était /static/ et le fichier js était dans /static/js/.

Une fois que j'ai déplacé mon fichier sw.js vers la portée de niveau supérieur /sw.js, les événements d'extraction ont tous été interceptés. En effet, l'étendue de la page à laquelle j'accédais avec mon navigateur / était identique à la portée de mon fichier sw.js /.

Veuillez me faire savoir si cela clarifie les choses pour les gens ou si je me trompe!

28
garviand

Le code exact dans l'article HTML5Rocks est

self.addEventListener('fetch', function(event) {
  event.respondWith(
    caches.match(event.request)
      .then(function(response) {
        // Cache hit - return response
        if (response) {
          return response;
        }

        // IMPORTANT: Clone the request. A request is a stream and
        // can only be consumed once. Since we are consuming this
        // once by cache and once by the browser for fetch, we need
        // to clone the response
        var fetchRequest = event.request.clone();

        return fetch(fetchRequest).then(
          function(response) {
            // Check if we received a valid response
            if(!response || response.status !== 200 || response.type !== 'basic') {
              return response;
            }

            // IMPORTANT: Clone the response. A response is a stream
            // and because we want the browser to consume the response
            // as well as the cache consuming the response, we need
            // to clone it so we have 2 stream.
            var responseToCache = response.clone();

            caches.open(CACHE_NAME)
              .then(function(cache) {
                cache.put(event.request, responseToCache);
              });

            return response;
          }
        );
      })
    );
});

La plus grande chose que je peux voir est que vous ne clonez pas le request à partir de l'extraction, vous devez le cloner car il est lu deux fois, une fois lorsqu'il est utilisé pour accéder au réseau (dans le fetch) et une fois lorsqu'il est utilisé comme clé du cache.

3
Kinlan

Si vous ne voyez pas la marque from service worker, Votre page n'est pas contrôlée par le technicien de service (vous pouvez vérifier en inspectant navigator.serviceWorker.controller Dans la page client). Le comportement par défaut d'une page est de devenir contrôlé la prochaine fois que vous la visiterez après l'activation de SW, vous avez donc deux options:

  • Actualisez la page après l'enregistrement.
  • Utilisez les méthodes self.skipWaiting() et self.clients.claim() pendant l'installation et l'activation respectivement pour forcer le technicien de service à prendre le contrôle des clients dès que possible.

Regardez le Service Worker Cookbook , il comprend un recette de cache JSON .

Une fois que vous avez résolu le problème avec le contrôle, vous devez corriger votre gestionnaire. Je pense que vous voulez appliquer une politique de cache-first. S'il n'est pas dans le cache, accédez au réseau et remplissez le cache. Pour faire ça:

self.onfetch = function (event) {
  var req = event.request;
  return event.respondWith(function cacheFirst() {
    // Open your cache.
    return self.caches.open('v1').then(function (cache) {
      // Check if the request is in there.
      return cache.match(req).then(function (res) {
        // If not match, there is no rejection but an undefined response.
        if (!res) {
          // Go to network.
          return fetch(req.clone()).then(function (res) {
            // Put in cache and return the network response.
            return cache.put(req, res.clone()).then(function () {
              return res;
            });
          });
        }
        return res;
      });
    });
  });
}
2
Salva

J'ai eu le même problème lorsque j'ai utilisé sw.js avec Django Framework. Ici J'ai trouvé une solution.

0
petriichuk