Service workers plus récents par défaut

Résumé

À partir de Chrome 68, les requêtes HTTP qui recherchent des mises à jour du script du service worker ne seront plus traitées par le cache HTTP par défaut. Cela permet de contourner un problème courant pour les développeurs, dans lequel le fait de définir un en-tête Cache-Control par inadvertance sur votre script de service worker peut entraîner des mises à jour retardées.

Si vous avez déjà désactivé le cache HTTP pour votre script /service-worker.js en le diffusant avec Cache-Control: max-age=0, vous ne devriez pas constater de changement en raison du nouveau comportement par défaut.

De plus, à partir de Chrome 78, la comparaison octet par octet sera appliquée aux scripts chargés dans un service worker via importScripts(). Toute modification apportée à un script importé déclenche le flux de mise à jour du service worker, comme le ferait une modification du service worker de niveau supérieur.

Contexte

Chaque fois que vous accédez à une nouvelle page qui se trouve dans le champ d'application d'un service worker, appelez explicitement registration.update() à partir de JavaScript ou lorsqu'un service worker est "réveillé" via un événement push ou sync, le navigateur demande en parallèle la ressource JavaScript qui a été transmise à l'appel navigator.serviceWorker.register() pour rechercher les mises à jour du script du service worker.

Pour les besoins de cet article, supposons que son URL soit /service-worker.js et qu'elle ne contienne qu'un seul appel à importScripts(), qui charge du code supplémentaire exécuté dans le service worker:

// Inside our /service-worker.js file:
importScripts('path/to/import.js');

// Other top-level code goes here.

Ce qui change

Avant Chrome 68, la requête de mise à jour de /service-worker.js était effectuée via le cache HTTP (comme la plupart des récupérations). Par conséquent, si le script a été envoyé à l'origine avec Cache-Control: max-age=600, les mises à jour au cours des 600 secondes suivantes (10 minutes) ne seront pas envoyées au réseau. L'utilisateur risque donc de ne pas recevoir la version la plus récente du service worker. Toutefois, si max-age était supérieur à 86 400 (24 heures), il serait traité comme s'il était égal à 86 400, afin d'éviter que les utilisateurs ne restent bloqués avec une version particulière pour toujours.

À partir de la version 68, le cache HTTP sera ignoré lors de la demande de mises à jour du script du service worker. Par conséquent, la fréquence des requêtes pour le script du service worker des applications Web existantes peut augmenter. Les requêtes pour importScripts passent toujours par le cache HTTP. Mais il ne s'agit que du comportement par défaut. Une nouvelle option d'enregistrement, updateViaCache, est disponible pour contrôler ce comportement.

updateViaCache

Les développeurs peuvent désormais transmettre une nouvelle option lors de l'appel de navigator.serviceWorker.register(): le paramètre updateViaCache. Il prend l'une des trois valeurs suivantes: 'imports', 'all' ou 'none'.

Les valeurs déterminent si et comment le cache HTTP standard du navigateur entre en jeu lors de l'envoi de la requête HTTP pour rechercher les ressources de service worker mises à jour.

  • Lorsque la valeur est 'imports', le cache HTTP n'est jamais consulté lors de la recherche de mises à jour du script /service-worker.js, mais il est consulté lors de la récupération de scripts importés (path/to/import.js, dans notre exemple). Il s'agit du comportement par défaut, qui correspond à celui de Chrome 68.

  • Lorsque ce paramètre est défini sur 'all', le cache HTTP est consulté lors de l'envoi de requêtes pour le script /service-worker.js de niveau supérieur, ainsi que pour tous les scripts importés dans le worker de service, comme path/to/import.js. Cette option correspond au comportement précédent de Chrome, avant Chrome 68.

  • Lorsque cette valeur est définie sur 'none', le cache HTTP n'est pas consulté lors de l'envoi de requêtes pour le /service-worker.js de niveau supérieur ni pour les scripts importés, tels que le path/to/import.js hypothétique.

Par exemple, le code suivant enregistre un service worker et s'assure que le cache HTTP n'est jamais consulté lors de la recherche de mises à jour du script /service-worker.js ou de tout script référencé via importScripts() dans /service-worker.js:

if ('serviceWorker' in navigator) {
  navigator.serviceWorker.register('/service-worker.js', {
    updateViaCache: 'none',
    // Optionally, set 'scope' here, if needed.
  });
}

Recherche les mises à jour des scripts importés

Avant Chrome 78, tout script de service worker chargé via importScripts() n'était récupéré qu'une seule fois (en vérifiant d'abord dans le cache HTTP ou via le réseau, en fonction de la configuration de updateViaCache). Après cette récupération initiale, il est stocké en interne par le navigateur et n'est jamais récupéré à nouveau.

Le seul moyen de forcer un service worker déjà installé à prendre en compte les modifications apportées à un script importé était de modifier l'URL du script, généralement en ajoutant une valeur semver (par exemple, importScripts('https://example.com/v1.1.0/index.js')) ou en incluant un hachage du contenu (par exemple, importScripts('https://example.com/index.abcd1234.js')). Un effet secondaire de la modification de l'URL importée est que le contenu du script de service worker de niveau supérieur change, ce qui déclenche à son tour le flux de mise à jour du service worker.

À partir de Chrome 78, chaque fois qu'une vérification de mise à jour est effectuée pour un fichier de service worker de niveau supérieur, des vérifications sont effectuées en même temps pour déterminer si le contenu de scripts importés a changé ou non. En fonction des en-têtes Cache-Control utilisés, ces vérifications de script importées peuvent être effectuées par le cache HTTP si updateViaCache est défini sur 'all' ou 'imports' (valeur par défaut), ou les vérifications peuvent être directement effectuées sur le réseau si updateViaCache est défini sur 'none'.

Si une vérification de mise à jour d'un script importé entraîne une différence octet par octet par rapport à ce qui était précédemment stocké par le service worker, cela déclenchera à son tour le flux de mise à jour complet du service worker, même si le fichier de service worker de niveau supérieur reste le même.

Le comportement de Chrome 78 correspond à ce que Firefox a implémenté il y a plusieurs années, dans Firefox 56. Safari implémente déjà ce comportement.

Que doivent faire les développeurs ?

Si vous avez désactivé le cache HTTP pour votre script /service-worker.js en l'exécutant avec Cache-Control: max-age=0 (ou une valeur similaire), vous ne devriez pas constater de modifications en raison du nouveau comportement par défaut.

Si vous diffusez votre script /service-worker.js avec la mise en cache HTTP activée, que ce soit intentionnellement ou parce que c'est simplement la valeur par défaut de votre environnement d'hébergement, vous constaterez peut-être une augmentation des requêtes HTTP supplémentaires pour /service-worker.js envoyées à votre serveur. Il s'agit de requêtes qui étaient auparavant traitées par le cache HTTP. Si vous souhaitez continuer à autoriser la valeur de l'en-tête Cache-Control à influencer la fraîcheur de votre /service-worker.js, vous devez commencer à définir explicitement updateViaCache: 'all' lors de l'enregistrement de votre service worker.

Étant donné qu'il peut y avoir un long traîne d'utilisateurs sur d'anciennes versions de navigateur, il est toujours conseillé de continuer à définir l'en-tête HTTP Cache-Control: max-age=0 sur les scripts de service worker, même si les navigateurs plus récents peuvent les ignorer.

Les développeurs peuvent profiter de cette opportunité pour décider s'ils souhaitent désactiver explicitement le stockage en cache HTTP pour leurs scripts importés dès maintenant et ajouter updateViaCache: 'none' à leur enregistrement de service worker, le cas échéant.

Diffuser des scripts importés

À partir de Chrome 78, les développeurs peuvent voir plus de requêtes HTTP entrantes pour les ressources chargées via importScripts(), car elles seront désormais vérifiées pour les mises à jour.

Si vous souhaitez éviter ce trafic HTTP supplémentaire, définissez des en-têtes Cache-Control durables lorsque vous diffusez des scripts qui incluent semver ou des hachages dans leurs URL, et utilisez le comportement updateViaCache par défaut de 'imports'.

Si vous souhaitez que vos scripts importés soient vérifiés pour détecter les mises à jour fréquentes, assurez-vous de les diffuser avec Cache-Control: max-age=0 ou d'utiliser updateViaCache: 'none'.

Documentation complémentaire

Les articles The Service Worker Lifecycle (Le cycle de vie des services workers) et Caching best practices & max-age gotchas (Bonnes pratiques de mise en cache et pièges liés à la valeur max-age), tous deux écrits par Jake Archibald, sont recommandés à tous les développeurs qui déploient des éléments sur le Web.