Mettre en cache les ressources pendant l'exécution

Certains éléments de votre application Web peuvent être peu utilisés, très volumineux ou varier en fonction de l'appareil (tels que les images responsives) ou de la langue de l'utilisateur. Dans ce cas, la prémise en cache peut être un anti-modèle. Vous devez donc vous fier à la mise en cache de l'environnement d'exécution à la place.

Dans Workbox, vous pouvez gérer la mise en cache des éléments à l'aide du module workbox-routing pour faire correspondre les routes, et les stratégies de mise en cache avec le module workbox-strategies.

Stratégies de mise en cache

Vous pouvez gérer la plupart des routes des éléments à l'aide de l'une des stratégies de mise en cache intégrées. Elles sont abordées en détail plus tôt dans cette documentation, mais en voici quelques-unes qui méritent d'être récapitulées:

  • La fonction Stalewhile Revalidate utilise une réponse mise en cache pour une requête si celle-ci est disponible et met à jour le cache en arrière-plan avec une réponse du réseau. Par conséquent, si l'élément n'est pas mis en cache, il attend la réponse du réseau et l'utilise. Cette stratégie est relativement sûre, car elle met régulièrement à jour les entrées de cache qui en dépendent. L'inconvénient est qu'elle demande systématiquement un élément au réseau en arrière-plan.
  • Réseau en premier tente d'abord d'obtenir une réponse du réseau. Si une réponse est reçue, elle est transmise au navigateur et enregistrée dans un cache. Si la requête réseau échoue, la dernière réponse mise en cache est utilisée, ce qui permet d'activer l'accès hors connexion à l'élément.
  • Cache First recherche d'abord une réponse dans le cache et l'utilise si elle est disponible. Si la requête ne se trouve pas dans le cache, le réseau est utilisé et toute réponse valide est ajoutée au cache avant d'être transmise au navigateur.
  • Réseau uniquement force la réponse à provenir du réseau.
  • Cache uniquement force la réponse à provenir du cache.

Vous pouvez appliquer ces stratégies pour sélectionner des requêtes à l'aide des méthodes proposées par workbox-routing.

Appliquer des stratégies de mise en cache avec la mise en correspondance des routes

workbox-routing expose une méthode registerRoute pour faire correspondre les routes et les gérer avec une stratégie de mise en cache. registerRoute accepte un objet Route qui accepte à son tour deux arguments:

  1. Chaîne, expression régulière ou rappel de correspondance permettant de spécifier des critères de correspondance d'itinéraire.
  2. Gestionnaire pour la route (il s'agit généralement d'une stratégie fournie par workbox-strategies).

Les rappels de mise en correspondance sont privilégiés pour établir une correspondance avec les routes, car ils fournissent un objet de contexte qui inclut l'objet Request, la chaîne d'URL de la requête, l'événement de récupération et une valeur booléenne indiquant si la requête est une requête de même origine.

Le gestionnaire gère ensuite la route correspondante. Dans l'exemple suivant, une route est créée pour correspondre aux requêtes d'images de même origine reçues, en appliquant le cache en premier lieu, en revenant à la stratégie réseau.

// sw.js
import { registerRoute, Route } from 'workbox-routing';
import { CacheFirst } from 'workbox-strategies';

// A new route that matches same-origin image requests and handles
// them with the cache-first, falling back to network strategy:
const imageRoute = new Route(({ request, sameOrigin }) => {
  return sameOrigin && request.destination === 'image'
}, new CacheFirst());

// Register the new route
registerRoute(imageRoute);

Utiliser plusieurs caches

Workbox vous permet de regrouper les réponses mises en cache dans des instances Cache distinctes à l'aide de l'option cacheName disponible dans les stratégies groupées.

Dans l'exemple suivant, les images utilisent une stratégie non actualisée pendant la revalidation, tandis que les éléments CSS et JavaScript utilisent une stratégie réseau basée sur le cache. La route de chaque élément place les réponses dans des caches distincts, en ajoutant la propriété cacheName.

// sw.js
import { registerRoute, Route } from 'workbox-routing';
import { CacheFirst, StaleWhileRevalidate } from 'workbox-strategies';

// Handle images:
const imageRoute = new Route(({ request }) => {
  return request.destination === 'image'
}, new StaleWhileRevalidate({
  cacheName: 'images'
}));

// Handle scripts:
const scriptsRoute = new Route(({ request }) => {
  return request.destination === 'script';
}, new CacheFirst({
  cacheName: 'scripts'
}));

// Handle styles:
const stylesRoute = new Route(({ request }) => {
  return request.destination === 'style';
}, new CacheFirst({
  cacheName: 'styles'
}));

// Register routes
registerRoute(imageRoute);
registerRoute(scriptsRoute);
registerRoute(stylesRoute);
Capture d'écran d'une liste d'instances de cache dans l'onglet "Application" des outils pour les développeurs Chrome. Trois caches distincts sont affichés: l'un nommé "scripts", l'autre nommé "styles" et le dernier, "images".
Visionneuse de l'espace de stockage du cache dans le panneau "Application" des outils pour les développeurs Chrome. Les réponses des différents types d'éléments sont stockées dans des caches distincts.

Définir une date d'expiration pour les entrées de cache

Tenez compte des quotas de stockage lorsque vous gérez le ou les caches des service workers. ExpirationPlugin simplifie la maintenance du cache et est exposé par workbox-expiration. Pour l'utiliser, spécifiez-le dans la configuration d'une stratégie de mise en cache:

// sw.js
import { registerRoute, Route } from 'workbox-routing';
import { CacheFirst } from 'workbox-strategies';
import { ExpirationPlugin } from 'workbox-expiration';

// Evict image cache entries older thirty days:
const imageRoute = new Route(({ request }) => {
  return request.destination === 'image';
}, new CacheFirst({
  cacheName: 'images',
  plugins: [
    new ExpirationPlugin({
      maxAgeSeconds: 60 * 60 * 24 * 30,
    })
  ]
}));

// Evict the least-used script cache entries when
// the cache has more than 50 entries:
const scriptsRoute = new Route(({ request }) => {
  return request.destination === 'script';
}, new CacheFirst({
  cacheName: 'scripts',
  plugins: [
    new ExpirationPlugin({
      maxEntries: 50,
    })
  ]
}));

// Register routes
registerRoute(imageRoute);
registerRoute(scriptsRoute);

Le respect des quotas de stockage peut s'avérer compliqué. Nous vous recommandons de tenir compte des utilisateurs qui subissent une pression de stockage ou qui souhaitent en faire le meilleur usage. Les paires ExpirationPlugin de Workbox peuvent aider à atteindre cet objectif.

Considérations sur les multi-origines

L'interaction entre votre service worker et vos éléments multi-origines est considérablement différente de celle avec des éléments de même origine. Le partage des ressources multi-origines (CORS, Cross-Origin Resource Sharing) est compliqué. Cette complexité s'étend à la façon dont vous gérez les ressources multi-origines dans un service worker.

Réponses opaques

Lorsque vous effectuez une requête multi-origine en mode no-cors, la réponse peut être stockée dans un cache de service worker et même utilisée directement par le navigateur. Toutefois, le corps de la réponse lui-même ne peut pas être lu via JavaScript. C'est ce qu'on appelle une réponse opaque.

Les réponses opaques sont une mesure de sécurité destinée à empêcher l'inspection d'un élément multi-origine. Vous pouvez toujours envoyer des requêtes d'éléments multi-origines et même les mettre en cache, mais vous ne pouvez pas lire le corps de la réponse ni même lire son code d'état.

N'oubliez pas d'activer le mode CORS

Même si vous chargez des éléments multi-origines qui définissent des en-têtes CORS permissifs qui vous permettent de lire les réponses, le corps de la réponse multi-origine peut rester opaque. Par exemple, le code HTML suivant déclenche des requêtes no-cors qui aboutissent à des réponses opaques, quels que soient les en-têtes CORS définis:

<link rel="stylesheet" href="https://example.com/path/to/style.css">
<img src="https://example.com/path/to/image.png">

Pour déclencher explicitement une requête cors qui générera une réponse non opaque, vous devez activer explicitement le mode CORS en ajoutant l'attribut crossorigin à votre code HTML:

<link crossorigin="anonymous" rel="stylesheet" href="https://example.com/path/to/style.css">
<img crossorigin="anonymous" src="https://example.com/path/to/image.png">

Gardez cela à l'esprit lorsque des routes de votre service worker mettent en cache des sous-ressources chargées au moment de l'exécution.

La boîte de travail ne peut pas mettre en cache les réponses opaques

Par défaut, Workbox adopte une approche prudente pour mettre en cache les réponses opaques. Étant donné qu'il est impossible d'examiner le code de réponse à la recherche de réponses opaques, la mise en cache d'une réponse d'erreur peut entraîner une expérience défaillante persistante si une stratégie de type "cache-first" ou "cache-only" est utilisée.

Si vous devez mettre en cache une réponse opaque dans Workbox, vous devez utiliser une stratégie axée sur le réseau ou obsolète pendant la validation pour la gérer. Oui, cela signifie que l'asset sera toujours demandé au réseau à chaque fois, mais que les réponses ayant échoué ne seront pas conservées et seront à terme remplacées par des réponses utilisables.

Si vous utilisez une autre stratégie de mise en cache et qu'une réponse opaque est renvoyée, Workbox vous avertit que la réponse n'a pas été mise en cache en mode Développement.

Forcer la mise en cache des réponses opaques

Si vous êtes absolument certain de vouloir mettre en cache une réponse opaque à l'aide d'une stratégie axée sur le cache ou uniquement sur le cache, vous pouvez forcer Workbox à le faire avec le module workbox-cacheable-response:

import {Route, registerRoute} from 'workbox-routing';
import {NetworkFirst, StaleWhileRevalidate} from 'workbox-strategies';
import {CacheableResponsePlugin} from 'workbox-cacheable-response';

const cdnRoute = new Route(({url}) => {
  return url === 'https://cdn.google.com/example-script.min.js';
}, new CacheFirst({
  plugins: [
    new CacheableResponsePlugin({
      statuses: [0, 200]
    })
  ]
}))

registerRoute(cdnRoute);

Réponses opaques et API navigator.storage

Pour éviter toute fuite d'informations interdomaines, une marge intérieure importante a été ajoutée à la taille d'une réponse opaque utilisée pour calculer les limites de quota de stockage. Cela affecte la manière dont l'API navigator.storage indique les quotas de stockage.

Cette marge intérieure varie selon les navigateurs, mais pour Chrome, la taille minimale d'une réponse opaque mise en cache dans l'espace de stockage global utilisé est d'environ 7 mégaoctets. Gardez cela à l'esprit lorsque vous déterminez le nombre de réponses opaques à mettre en cache, car vous pourriez facilement dépasser les quotas de stockage bien plus tôt que prévu.