Service workers plus récents par défaut

tl;dr

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

Si vous avez déjà désactivé la mise en cache HTTP pour votre script /service-worker.js en le diffusant avec Cache-Control: max-age=0, vous ne devriez constater aucun 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 premier niveau.

Contexte

Chaque fois que vous accédez à une nouvelle page relevant du champ d'application d'un service worker, appelez explicitement registration.update() à partir de JavaScript, ou lorsqu'un service worker est "activé" via un événement push ou sync, le navigateur demandera, en parallèle, la ressource JavaScript initialement 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 est /service-worker.js et qu'elle contient 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 le sont la plupart des extractions). Autrement dit, si le script était envoyé à l'origine avec Cache-Control: max-age=600, les mises à jour effectuées dans les 600 secondes (10 minutes) suivantes n'étaient pas transmises au réseau. Par conséquent, l'utilisateur risquait de ne pas recevoir la version la plus récente du service worker. Toutefois, si la valeur de max-age était supérieure à 86 400 (24 heures), elle serait traitée comme s'il s'agissait de 86 400, pour éviter que les utilisateurs soient bloqués indéfiniment avec une version particulière.

À partir de 68, le cache HTTP sera ignoré lors de la demande de mises à jour du script de service worker. Par conséquent, les applications Web existantes pourront constater une augmentation de la fréquence des requêtes pour leur script de service worker. Les requêtes destinées à importScripts seront toujours traitées par le cache HTTP. Mais il s'agit simplement de la valeur par défaut. Il s'agit d'une nouvelle option d'enregistrement, updateViaCache, qui permet de 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 accepte 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'exécution de la requête HTTP pour rechercher les ressources de service worker mises à jour.

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

  • Si 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 premier niveau, ainsi que pour tous les scripts importés dans le service worker, comme path/to/import.js. Cette option correspond au comportement précédent dans Chrome, avant Chrome 68.

  • Si défini sur 'none', le cache HTTP n'est pas consulté lors de requêtes pour le /service-worker.js de premier niveau ou pour des scripts importés, comme le path/to/import.js hypothétique.

Par exemple, le code suivant enregistre un service worker et garantit que le cache HTTP n'est jamais consulté lors de la recherche de mises à jour du script /service-worker.js ou des scripts référencés 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 de 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 commençant par le cache HTTP ou via le réseau, en fonction de la configuration updateViaCache). Après cette récupération initiale, elles sont stockées en interne par le navigateur et ne sont jamais récupérées.

Le seul moyen de forcer un service worker déjà installé à récupérer 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')). Si vous modifiez l'URL importée, le contenu du script de service worker de premier niveau est alors modifié, ce qui déclenche à son tour le flux 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 premier niveau, des vérifications sont effectuées en même temps pour déterminer si le contenu des scripts importés a été modifié. Selon les en-têtes Cache-Control utilisés, ces vérifications de script importé 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 aller directement au réseau si updateViaCache est défini sur 'none'.

Si une vérification des mises à 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éclenche à son tour le flux de mise à jour complet du service worker, même si le fichier de service worker de premier niveau reste le même.

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

Que doivent faire les développeurs ?

Si vous avez désactivé la mise en cache HTTP pour votre script /service-worker.js en le diffusant avec Cache-Control: max-age=0 (ou une valeur similaire), vous ne devriez constater aucun changement dû au 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 qu'il s'agit simplement de la valeur par défaut pour votre environnement d'hébergement, vous constaterez peut-être une augmentation des requêtes HTTP supplémentaires pour /service-worker.js effectuées sur votre serveur. Il s'agit de requêtes qui étaient traitées par le cache HTTP. Si vous souhaitez continuer à autoriser la valeur de l'en-tête Cache-Control à influencer l'actualisation 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 une longue traîne d'utilisateurs sur d'anciennes versions de navigateur, il est toujours judicieux 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 explicitement désactiver la mise en cache HTTP pour leurs scripts importés et ajouter updateViaCache: 'none' à l'enregistrement de leur service worker, le cas échéant.

Diffuser des scripts importés

À partir de Chrome 78, les développeurs pourront 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 de longue durée lorsque vous diffusez des scripts dont les URL incluent le code semver ou le hachage, 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 des mises à jour fréquentes, assurez-vous de les diffuser avec Cache-Control: max-age=0 ou d'utiliser updateViaCache: 'none'.

Complément d'informations

Les sections "The Service Worker Lifecycle" et "Caching best Practices & max-age getchas" (Bonnes pratiques de mise en cache et pièges à l'âge maximal) de Jake Archibld sont recommandées pour tous les développeurs qui déploient des solutions sur le Web.