Optimisation de la vitesse

Service workers et performance : utiliser le cache sans masquer un contenu périmé

Définissez les besoins de chaque ressource avant de multiplier les couches de livraison.

Un navigateur relié à un worker représenté par un engrenage, un cache et un serveur
Illustration conceptuelle des accès au cache et au réseau gérés par un service worker.

Un service worker peut rendre les visites suivantes plus robustes et éviter de répéter certains échanges réseau. Son installation ne suffit pourtant pas à améliorer les Core Web Vitals. Il faut déterminer quelles requêtes il doit traiter, quel degré de fraîcheur exigent les réponses et ce qui se passe lors du déploiement suivant. Commencez par un ensemble limité de ressources publiques. Élargissez ce périmètre lorsque les tests montrent un bénéfice qui justifie son entretien.

Distinguer le worker du cache

Un service worker est un script piloté par des événements, capable d'intercepter les requêtes des pages qu'il contrôle. Cache Storage conserve séparément des couples de requêtes et de réponses. Aucun des deux ne remplace le cache HTTP du navigateur. Une requête effectuée par le worker peut encore utiliser ce cache HTTP. Identifiez donc la couche qui a fourni la réponse avant d'attribuer un gain à votre code.

Cache Storage n'applique pas automatiquement les règles de fraîcheur HTTP. Votre application décide quand stocker, actualiser et supprimer les entrées. Un champ de réponse HTTP envoyé par le serveur ne remplace pas cette politique. Si le but est simplement de réutiliser des feuilles de style et des images publiques, vérifiez d'abord la configuration du cache navigateur avant d'ajouter une couche programmable.

Choisir une stratégie selon la ressource

Pour des fichiers publics dont l'URL contient une empreinte du contenu, consulter le cache avant le réseau constitue un point de départ raisonnable. Une URL inchangée désigne alors un contenu inchangé. Cette logique ne convient pas automatiquement aux données de compte, aux disponibilités ou au HTML qui doit refléter un nouveau déploiement.

Donner la priorité au réseau favorise la fraîcheur, mais peut faire attendre le visiteur lorsque la connexion est défaillante. Définissez explicitement l'annulation, la réponse de secours et le traitement des échecs. Servir une réponse enregistrée tout en la renouvelant en arrière plan accélère sa réutilisation, mais le visiteur actuel peut recevoir l'ancienne version. Cette stratégie génère aussi du trafic même si une copie existe déjà.

Ce sont des décisions de produit. Une page d'aide accessible hors ligne peut rendre service ; une ancienne confirmation de paiement peut induire en erreur. N'appliquez pas une stratégie universelle sous prétexte que toutes les requêtes traversent le même gestionnaire.

Ne pas oublier la première visite

Enregistrement, installation, activation et contrôle sont des états différents. Un worker nouvellement enregistré ne contrôle normalement pas la page initiale qui l'a enregistré. Les visites suivantes peuvent en profiter lorsqu'il est prêt, tandis qu'un nouveau visiteur dépend encore du chargement habituel.

Une mise en cache préalable trop large peut concurrencer les ressources nécessaires à la page actuelle. Ne préparez que ce qui sert une expérience définie. Améliorez indépendamment la réponse du serveur, la découverte des ressources et le rendu. Le guide sur le temps de réponse du serveur reste pertinent même lorsqu'une visite déjà en cache semble rapide.

Commencer par un exemple au périmètre explicite

Ce worker illustratif prépare une liste explicite de fichiers publics et répond aux requêtes correspondantes depuis le cache lorsque cela est possible. Placez ce code dans un fichier de worker, enregistrez ce fichier séparément dans un contexte sécurisé et remplacez les chemins par de vrais fichiers. Les réponses doivent être publiques et indépendantes de l'état de connexion au compte.

javascript
const CACHE = "editorialAssetsRelease";
const ASSETS = [
  "/assets/site.abc123.css",
  "/assets/brand.def456.svg"
];

self.addEventListener("install", event => {
  event.waitUntil(
    caches.open(CACHE).then(cache => cache.addAll(ASSETS))
  );
});

self.addEventListener("fetch", event => {
  const request = event.request;
  const url = new URL(request.url);
  if (request.method !== "GET" ||
      url.origin !== self.location.origin ||
      !ASSETS.includes(url.pathname + url.search)) return;

  event.respondWith((async () => {
    let cached;
    try {
      const cache = await caches.open(CACHE);
      cached = await cache.match(request);
    } catch {}
    return cached || fetch(request);
  })());
});

Lors d'un déploiement, modifiez ensemble le nom du cache et les URL contenant une empreinte du contenu. L'échec d'une requête indispensable à cette préparation peut faire échouer l'installation. Cet exemple n'inclut ni page HTML de secours, ni cache qui grossit pendant l'utilisation, ni prise de contrôle automatique après une mise à jour. Son périmètre reste ainsi clair. Une intégration réelle nécessite encore l'enregistrement, la gestion des mises à jour et le retrait des caches propres à l'application.

Préserver la compatibilité avec les onglets ouverts

Un worker récemment installé peut attendre pendant que le worker actif sert les clients existants. Ce comportement évite de mélanger une ancienne page avec de nouvelles ressources incompatibles. skipWaiting() et clients.claim() modifient le cycle de vie et le contrôle des pages. Ce ne sont pas des optimisations de vitesse à appliquer systématiquement.

Choisissez entre proposer une mise à jour, attendre un rechargement naturel ou prendre en charge des versions compatibles en parallèle. Conservez les anciennes ressources identifiées par leur contenu tant que des pages existantes peuvent les demander. Ne supprimez que les caches appartenant à votre application, avec une politique qui tient compte des clients actifs. Consultez la documentation du cycle de vie des service workers pour concevoir cette transition.

Protéger le contenu privé et prévoir les pertes de stockage

Écartez du cache l'authentification, les pages de compte et les réponses d'API sensibles, sauf si une conception examinée spécifiquement l'exige. Une liste explicite d'URL autorisées est plus facile à contrôler que le stockage de toute requête réussie. Ne supposez pas qu'un cookie ou une directive HTTP sécurise un gestionnaire général de Cache Storage.

Si l'application conserve volontairement des contenus propres à un utilisateur, concevez ensemble la déconnexion, le changement de compte et la suppression. Prévoyez également l'éviction des ressources par le navigateur. Le mode hors ligne doit expliquer ce qui reste utilisable et ce qui exige une connexion. L'absence de données ne doit jamais devenir une action présentée comme réussie.

Tester des états différents, pas seulement un cache chaud

Comparez une visite vierge, une visite contrôlée avec stockage rempli, une visite hors ligne et une mise à jour avec un ancien onglet ouvert. Inspectez la portée et le contrôleur du worker, les entrées réelles de Cache Storage et la provenance des réponses. Désactiver le cache HTTP dans les outils de développement ne prouve pas que Cache Storage a été contourné.

Utilisez une trace pour comprendre un ralentissement et les mesures de terrain pour vérifier ses conséquences sur les visiteurs. Les limites d'une bonne expérience sont un LCP inférieur ou égal à 2.5 s, un INP inférieur ou égal à 200 ms et un CLS inférieur ou égal à 0.1, évalués au 75e percentile en séparant mobile et ordinateur. Une visite locale répétée et rapide ne démontre pas que la distribution de terrain respecte ces limites.

Conservez le worker lorsque son apport est clair. Des contenus corrects, des mises à jour prévisibles et une reprise fiable font partie du travail de performance. Le guide sur les données de terrain et de laboratoire aide à replacer chaque observation dans son contexte.

Guides associés