Stratégies de mise en cache des service workers

Jusqu'à présent, il n'y avait que des mentions et de minuscules extraits de code Interface Cache. Pour utiliser efficacement les service workers, il faut adopter une ou plusieurs stratégies de mise en cache, ce qui nécessite quelques connaissances sur l'interface Cache.

Une stratégie de mise en cache est une interaction entre l'événement fetch d'un service worker et l'interface Cache. La façon dont une stratégie de mise en cache est écrite dépend par exemple, il peut être préférable de gérer les requêtes d'éléments statiques différemment des demandes de documents, et cela affecte la composition d'une stratégie de mise en cache.

Avant d'aborder les stratégies, Prenons une seconde pour découvrir ce qu'est l'interface Cache, ce qu'elle est. et un aperçu de certaines des méthodes qu'il propose pour gérer les caches de service workers.

Comparaison entre l'interface Cache et le cache HTTP

Si vous n'avez jamais travaillé avec l'interface Cache auparavant, il pourrait être tentant de le considérer comme la même chose que : ou du moins au cache HTTP. Ce n'est pas le cas.

  • L'interface Cache est un mécanisme de mise en cache entièrement distinct du cache HTTP.
  • Quel que soit Cache-Control que vous utilisez pour influencer le cache HTTP n'a aucune incidence sur les éléments stockés dans l'interface Cache.

Il est utile de considérer les caches des navigateurs comme des couches. Le cache HTTP est un cache de bas niveau alimenté par des paires clé-valeur avec des instructions exprimées dans des en-têtes HTTP.

En revanche, l'interface Cache est un cache de haut niveau géré par une API JavaScript. Cela offre plus de flexibilité que lorsque vous utilisez des paires clé-valeur HTTP relativement simplistes, et c'est la moitié de ce qui rend les stratégies de mise en cache possibles. Voici quelques méthodes d'API importantes concernant les caches de service workers:

  • CacheStorage.open pour créer une instance Cache.
  • Cache.add et Cache.put pour stocker les réponses du réseau dans un cache de service worker.
  • Cache.match pour localiser une réponse mise en cache dans une instance Cache.
  • Cache.delete pour supprimer une réponse mise en cache d'une instance Cache.

En voici quelques-uns. Il existe d'autres méthodes utiles, mais ce sont les fonctions de base que vous verrez utilisées plus loin dans ce guide.

L'événement fetch modeste

L'autre moitié d'une stratégie de mise en cache est l'authentification Événement fetch. Jusqu'à présent, dans cette documentation, vous avez un peu entendu parler de l'« interception des requêtes réseau », et l'événement fetch dans un service worker est l'endroit où cela se produit:

// Establish a cache name
const cacheName = 'MyFancyCacheName_v1';

self.addEventListener('install', (event) => {
  event.waitUntil(caches.open(cacheName));
});

self.addEventListener('fetch', async (event) => {
  // Is this a request for an image?
  if (event.request.destination === 'image') {
    // Open the cache
    event.respondWith(caches.open(cacheName).then((cache) => {
      // Respond with the image from the cache or from the network
      return cache.match(event.request).then((cachedResponse) => {
        return cachedResponse || fetch(event.request.url).then((fetchedResponse) => {
          // Add the network response to the cache for future visits.
          // Note: we need to make a copy of the response to save it in
          // the cache and use the original as the request response.
          cache.put(event.request, fetchedResponse.clone());

          // Return the network response
          return fetchedResponse;
        });
      });
    }));
  } else {
    return;
  }
});

Voici un exemple de jouet, et que vous pouvez constater par vous-même, elle offre un aperçu de ce que les service workers peuvent faire. Le code ci-dessus effectue les opérations suivantes:

  1. Inspectez la propriété destination de la requête pour voir s'il s'agit d'une requête d'image.
  2. Si l'image se trouve dans le cache du service worker, diffusez-la à partir de cet emplacement. Sinon, récupérez l'image sur le réseau, stocker la réponse dans le cache et renvoyer la réponse du réseau.
  3. Toutes les autres requêtes sont transmises par le service worker sans interaction avec le cache.

L'objet event d'une extraction contient Propriété request quelques informations utiles pour vous aider à identifier le type de chaque requête:

  • url, qui est l'URL de la requête réseau actuellement gérée par l'événement fetch.
  • method, qui est la méthode de requête (par exemple, GET ou POST).
  • mode, qui décrit le mode de la requête. La valeur 'navigate' est souvent utilisée pour distinguer les demandes de documents HTML des autres demandes.
  • destination, qui décrit le type de contenu demandé sans utiliser l'extension de fichier de l'élément demandé.

Encore une fois, le nom du jeu est asynchrone. Rappelez-vous que l'événement install propose une event.waitUntil qui reçoit une promesse et attend sa résolution avant de passer à l'activation. L'événement fetch offre une fonctionnalité Méthode event.respondWith que vous pouvez utiliser pour renvoyer le résultat d'une requête Demande fetch ou une réponse renvoyée par l'interface Cache méthode match.

Stratégies de mise en cache

Maintenant que vous vous êtes familiarisé avec les instances Cache et le gestionnaire d'événements fetch, vous êtes prêt à vous plonger dans les stratégies de mise en cache des service workers. Bien que les possibilités soient pratiquement infinies, Ce guide s'appuiera sur les stratégies fournies avec Workbox, afin que vous puissiez avoir une idée de ce qui se passe dans les composants internes de Workbox.

Cache uniquement

Affiche le flux de la page vers le service worker et le cache.

Commençons par une stratégie simple de mise en cache que nous appellerons "Cache uniquement". C'est simple: quand le service worker contrôle la page, les requêtes correspondantes ne seront que transmises au cache. Cela signifie que tous les éléments mis en cache devront être mis en pré-cache pour que le format fonctionne. et que ces éléments ne seront jamais mis à jour dans le cache tant que le service worker n'aura pas été mis à jour.

// Establish a cache name
const cacheName = 'MyFancyCacheName_v1';

// Assets to precache
const precachedAssets = [
  '/possum1.jpg',
  '/possum2.jpg',
  '/possum3.jpg',
  '/possum4.jpg'
];

self.addEventListener('install', (event) => {
  // Precache assets on install
  event.waitUntil(caches.open(cacheName).then((cache) => {
    return cache.addAll(precachedAssets);
  }));
});

self.addEventListener('fetch', (event) => {
  // Is this one of our precached assets?
  const url = new URL(event.request.url);
  const isPrecachedRequest = precachedAssets.includes(url.pathname);

  if (isPrecachedRequest) {
    // Grab the precached asset from the cache
    event.respondWith(caches.open(cacheName).then((cache) => {
      return cache.match(event.request.url);
    }));
  } else {
    // Go to the network
    return;
  }
});

Ci-dessus, un tableau d'éléments est mis en pré-cache au moment de l'installation. Lorsque le service worker traite les récupérations, nous vérifions si l'URL de requête gérée par l'événement fetch se trouve dans le tableau d'éléments mis en pré-cache. Si tel est le cas, nous récupérons la ressource dans le cache et ignorons le réseau. Les autres requêtes passent par le réseau, et uniquement le réseau. Pour voir cette stratégie en action, Regardez cette démonstration avec la console ouverte.

Réseau uniquement

Affiche le flux de la page vers le service worker et le réseau.

Le contraire de "Cache uniquement" est "Réseau uniquement", où une requête est transmise au réseau par un service worker sans interaction avec le cache du service worker. Il s'agit d'une bonne stratégie pour garantir l'actualisation du contenu (balisage, par exemple). mais en contrepartie, cela ne fonctionnera jamais lorsque l'utilisateur est hors connexion.

S'assurer qu'une requête est transmise au réseau signifie simplement que vous n'appelez pas event.respondWith pour une requête de mise en correspondance. Si vous voulez être explicite, vous pouvez ajouter un return; vide dans votre rappel d'événement fetch pour les requêtes que vous souhaitez transmettre au réseau. Voici ce qu'il se passe dans le mode "Cache uniquement" : pour les requêtes qui ne sont pas mises en pré-cache.

Mettre en cache d'abord, en revenant au réseau

Affiche le flux de la page, au service worker, au cache, puis au réseau si ce n'est pas le cas.

Avec cette stratégie, les choses deviennent un peu plus complexes. Pour les requêtes correspondantes, le processus est le suivant:

  1. La requête est mise en cache. Si l'élément se trouve dans le cache, diffusez-le à partir de là.
  2. Si la requête ne se trouve pas dans le cache, accédez au réseau.
  3. Une fois la requête réseau terminée, ajoutez-la au cache, puis renvoie la réponse à partir du réseau.

Voici un exemple de cette stratégie, que vous pouvez tester Une démonstration en direct:

// Establish a cache name
const cacheName = 'MyFancyCacheName_v1';

self.addEventListener('fetch', (event) => {
  // Check if this is a request for an image
  if (event.request.destination === 'image') {
    event.respondWith(caches.open(cacheName).then((cache) => {
      // Go to the cache first
      return cache.match(event.request.url).then((cachedResponse) => {
        // Return a cached response if we have one
        if (cachedResponse) {
          return cachedResponse;
        }

        // Otherwise, hit the network
        return fetch(event.request).then((fetchedResponse) => {
          // Add the network response to the cache for later visits
          cache.put(event.request, fetchedResponse.clone());

          // Return the network response
          return fetchedResponse;
        });
      });
    }));
  } else {
    return;
  }
});

Bien que cet exemple ne concerne que les images, cette stratégie est efficace à appliquer à tous les éléments statiques (CSS, JavaScript, images et polices), en particulier ceux dont la version est hachée. Elle accélère les composants immuables en ignorant toute vérification d'actualisation du contenu auprès du serveur que le cache HTTP peut lancer. Plus important encore, tous les éléments mis en cache seront disponibles hors connexion.

Premièrement, le réseau, en revenant au cache

Affiche le flux de la page, le service worker, le réseau, puis la mise en cache si le réseau n'est pas disponible.

Si vous deviez inverser « Le cache d'abord, puis le réseau » sur sa tête, on obtient le type "Réseau d'abord, deuxième cache" comme vous le voyez:

  1. Vous accédez d'abord au réseau pour une requête, puis vous placez la réponse dans le cache.
  2. Si vous êtes hors connexion par la suite, vous revenez à la dernière version de cette réponse dans le cache.

Cette stratégie est idéale pour les requêtes HTML ou API en ligne, vous voulez connaître la version la plus récente d'une ressource, mais souhaitez accorder un accès hors connexion à la version la plus récente disponible. Voici ce à quoi cela pourrait ressembler lorsqu'il est appliqué à des demandes pour HTML:

// Establish a cache name
const cacheName = 'MyFancyCacheName_v1';

self.addEventListener('fetch', (event) => {
  // Check if this is a navigation request
  if (event.request.mode === 'navigate') {
    // Open the cache
    event.respondWith(caches.open(cacheName).then((cache) => {
      // Go to the network first
      return fetch(event.request.url).then((fetchedResponse) => {
        cache.put(event.request, fetchedResponse.clone());

        return fetchedResponse;
      }).catch(() => {
        // If the network is unavailable, get
        return cache.match(event.request.url);
      });
    }));
  } else {
    return;
  }
});

Vous pouvez essayer cette fonctionnalité lors d'une démonstration. Tout d'abord, accédez à la page. Vous devrez peut-être actualiser la page avant de placer la réponse HTML dans le cache. Ensuite, dans les outils pour les développeurs, simuler une connexion hors connexion ; et actualisez-le de nouveau. La dernière version disponible sera diffusée instantanément à partir du cache.

Dans les cas où la capacité hors connexion est importante, mais vous devez trouver un équilibre entre ces capacités et l'accès à la version la plus récente d'un élément de balisage ou de données d'API. "Le réseau d'abord, puis le cache" est une stratégie solide qui permet d'atteindre cet objectif.

Obsolète pendant la revalidation

Affiche le flux de la page, au service worker, au cache, puis du réseau au cache.

Parmi les stratégies évoquées jusqu'à présent, est le plus complexe. Elle est similaire aux deux dernières stratégies d’une certaine manière, mais la procédure donne la priorité à la vitesse d'accès à une ressource, tout en les maintenant à jour en arrière-plan. Cette stratégie ressemble à ceci:

  1. Lors de la première demande d'élément, récupérez-le sur le réseau, le placer dans le cache et renvoyer la réponse du réseau.
  2. Lors des requêtes suivantes, diffusez d'abord l'élément à partir du cache, puis "en arrière-plan". redemander l'élément sur le réseau et mettre à jour l'entrée de cache de l'élément.
  3. Pour les requêtes ultérieures, vous recevrez la dernière version extraite du réseau, qui a été placée dans le cache lors de l'étape précédente.

C'est une excellente stratégie pour les mises à jour qu'il est important de garder à jour, mais elles ne sont pas cruciales. Pensez à des choses comme des avatars pour un site de médias sociaux. Elles sont mises à jour lorsque les utilisateurs se déplacent, mais la dernière version n'est pas strictement nécessaire à chaque demande.

// Establish a cache name
const cacheName = 'MyFancyCacheName_v1';

self.addEventListener('fetch', (event) => {
  if (event.request.destination === 'image') {
    event.respondWith(caches.open(cacheName).then((cache) => {
      return cache.match(event.request).then((cachedResponse) => {
        const fetchedResponse = fetch(event.request).then((networkResponse) => {
          cache.put(event.request, networkResponse.clone());

          return networkResponse;
        });

        return cachedResponse || fetchedResponse;
      });
    }));
  } else {
    return;
  }
});

Vous pouvez voir cela en action dans mais une autre démonstration en direct, en particulier si vous regardez l'onglet "Réseau" dans les outils pour les développeurs de votre navigateur, et sa visionneuse CacheStorage (si les outils pour les développeurs de votre navigateur en proposent un tel outil).

En avant vers Workbox !

Ce document conclut notre examen de l'API Service Worker, ainsi que les API associées, Vous en savez donc suffisamment sur l'utilisation directe des service workers pour commencer à bricoler avec Workbox.