MAISON CODE .
/ PWA · Offline · Caching · Performance · Architecture

Service Workers : le proxy réseau programmable

Transformez votre site Web en une application compatible hors ligne. Un guide technique sur le cycle de vie de Service Worker, les stratégies de mise en cache (Workbox) et la synchronisation en arrière-plan.

AB
Alex B.
Service Workers : le proxy réseau programmable

La plus grande différence entre une application native et une application Web est la résilience. Si vous ouvrez Instagram en mode avion, vous voyez les photos mises en cache. Si vous ouvrez un site Web standard en mode avion, vous voyez le dinosaure.

C’est inacceptable pour les logiciels modernes. Les Service Workers sont la solution. Il s’agit d’un script de travail JavaScript qui s’exécute en arrière-plan, distinct d’une page Web. Ils agissent en tant que proxy réseau côté client.

Chez Maison Code Paris, nous construisons des Progressive Web Apps (PWA) qui réussissent le « Subway Test » : L’utilisateur peut-il parcourir le catalogue sous terre ? Si la réponse est non, l’application est en panne.

Pourquoi Maison Code en parle

Chez Maison Code Paris, nous agissons comme la conscience architecturale de nos clients. Nous héritons souvent de stacks “modernes” construites sans compréhension fondamentale de l’échelle.

Nous abordons ce sujet car il représente un point de pivot critique dans la maturité de l’ingénierie. Une mise en œuvre correcte différencie un MVP fragile d’une plateforme résiliente de niveau entreprise.

Pourquoi Maison Code exige d’abord le mode hors ligne

Un écran blanc est un client perdu. Nous traitons « Hors ligne » comme une fonctionnalité et non comme un état d’erreur. Notre norme PWA garantit :

  • Résilience : le shell de l’application se charge instantanément, même en 2G.
  • Engagement : les utilisateurs peuvent ajouter au panier lorsqu’ils sont hors ligne (Background Sync l’envoie plus tard).
  • Rétention : les notifications push génèrent un taux de retour 3 fois plus élevé que celui des e-mails pour nos clients mobiles. Nous ne construisons pas de « sites Web » ; nous construisons des « applications Web installables ».

Le modèle mental : l’homme du milieu

Normalement : Navigateur -> Réseau -> Serveur. Avec SW : Navigateur -> Service Worker. Le Service Worker décide alors :

  1. “J’ai ceci dans le cache. Renvoyez le cache.” (latence de 0 ms).
  2. “Je dois accéder au Réseau.” (Latence standard).
  3. “Allez au réseau, mais en cas d’échec, renvoyez le cache.” (Élasticité).

Cette logique est programmable. Vous contrôlez chaque octet.

Le cycle de vie (la partie la plus difficile)

Les Service Workers sont notoirement difficiles à déboguer car ils vivent indépendamment de l’onglet.

  1. Installer : le navigateur télécharge « sw.js ». Il télécharge les actifs critiques (Precache).
  2. Activer : Le logiciel démarre. Il nettoie les anciens caches. Surtout, il ne contrôle pas encore les onglets ouverts.
  3. Réclamation : Vous devez appeler clients.claim() pour prendre immédiatement le contrôle des onglets ouverts.
  4. Fetch : Le logiciel crée un écouteur d’événement fetch.

Le problème de la “nouvelle version”

Vous déployez v2. L’utilisateur visite le site. Le navigateur voit « sw.js » modifié. Il installe « v2 » en arrière-plan. Mais la « v1 » exécute toujours la page ! v2 entre dans l’état « En attente ». Il ne s’activera que lorsque tous les onglets seront fermés. Pour résoudre ce problème, nous implémentons un toast « Mise à jour disponible » dans l’interface utilisateur.

// Dans votre application React
if (inscription.en attente) {
  showToast("Mise à jour disponible", () => {
    inscription.waiting.postMessage({ type : 'SKIP_WAITING' });
    window.location.reload();
  });
}

## Stratégies de mise en cache (à l’aide de Workbox)

L’écriture d’une logique de mise en cache brute est sujette aux erreurs. Nous utilisons Google Workbox.

1. Pré-cache (le shell de l’application)

Nous téléchargeons le squelette HTML, le logo et le bundle JS principal lors de l’installation. Ces fichiers sont « épinglés » dans le cache. Ils sont assurés d’être là.

importer { precacheAndRoute } depuis 'workbox-precaching' ;
// __WB_MANIFEST est injecté par l'outil de build (Webpack/Vite)
precacheAndRoute(self.__WB_MANIFEST);

2. Périmé pendant la revalidation (contenu dynamique)

Pour les appels API (/api/products), nous voulons de la rapidité mais aussi de la fraîcheur.

  • Étape 1 : Renvoyez immédiatement le JSON mis en cache. (L’application s’affiche en 50 ms).
  • Étape 2 : Récupérez le nouveau JSON à partir du réseau.
  • Étape 3 : Mettre à jour le cache.
  • Étape 4 : Diffusez la mise à jour vers l’interface utilisateur (“Nouveaux prix disponibles”).
importer {registerRoute} depuis 'workbox-routing' ;
importer { StaleWhileRevalidate } depuis 'workbox-strategies' ;

s'inscrireRoute(
  ({ url }) => url.pathname.startsWith('/api/'),
  nouveau StaleWhileRevalidate({
    nom du cache : 'api-cache',
    plugins : [
      new ExpirationPlugin({ maxEntries : 50, maxAgeSeconds : 3600 }), // Conserver pendant 1 heure
    ],
  })
);

3. Cache d’abord (actifs immuables)

Pour les images de produits (qui sont hébergées sur des CDN avec des URL versionnées), nous utilisons Cache First. S’il est dans le cache, renvoyez-le. Ne vous connectez jamais au réseau.

Stockage hors ligne : IndexedDB

LocalStorage est synchrone. Il bloque le thread principal. Les Service Workers sont asynchrones. Ils ne peuvent pas accéder à LocalStorage. Vous devez utiliser IndexedDB. Il s’agit d’une base de données NoSQL à l’intérieur du navigateur. Nous l’utilisons pour stocker les “Actions en attente”.

Scénario : l’utilisateur clique sur “Ajouter au panier” lorsqu’il est hors ligne.

  1. L’application détecte hors ligne.
  2. L’application écrit l’action dans IndexedDB : { type : 'ADD_TO_CART', id : 123 }.
  3. L’application met à jour l’interface utilisateur de manière optimiste (le badge affiche “1”).
  4. Service Worker enregistre un événement Background Sync.

Synchronisation en arrière-plan

C’est la fonctionnalité qui tue. Lorsque la connexion est rétablie (même si l’utilisateur a fermé l’application !), le système d’exploitation réveille le Service Worker.

// sw.js
self.addEventListener('sync', (événement) => {
  if (event.tag === 'sync-cart') {
    event.waitUntil(syncCart());
  }
});

fonction asynchrone syncCart() {
  const db = wait openDB('mon-magasin');
  const action = attendre db.get('en attente', 'cart');
  wait fetch('/api/cart', { méthode : 'POST', corps : JSON.stringify(action) });
}

Pièges courants

  1. Mise en cache du Service Worker lui-même : Assurez-vous que votre serveur envoie « Cache-Control : no-cache » pour « sw.js ». Si le navigateur met en cache le fichier SW pendant 24 heures, vous ne pouvez pas déployer de mises à jour pendant 24 heures.
  2. Réponses opaques (CORS) : Si vous récupérez une image depuis « cdn.shopify.com » sans en-têtes CORS, le logiciel voit une « réponse opaque ». Il ne peut pas vérifier le code d’état. Il peut mettre en cache une page d’erreur 404 sous forme d’image. Configurez toujours CORS sur vos buckets.
  3. Quota dépassé : Les navigateurs limitent le stockage (généralement 10 à 20 % de l’espace disque). Mettez toujours en œuvre les politiques d’expulsion les moins récemment utilisées (LRU) (à l’aide du plug-in d’expiration Workbox).

10. Synchronisation périodique en arrière-plan

La synchronisation standard fonctionne lorsque la connexion revient. Periodic Sync permet à l’application de se réveiller en arrière-plan (par exemple, une fois toutes les 24 heures) pour récupérer du nouveau contenu. Imaginez une application d’actualités. Vous souhaitez que l’utilisateur ait les gros titres du matin avant d’ouvrir l’application. registration.periodicSync.register('get-headlines', { minInterval: 24 * 60 * 60 * 1000 });. Remarque : Cela nécessite généralement que la PWA soit installée sur l’écran d’accueil.

11. Conseils de débogage (Chrome DevTools)

L’onglet “Application” de Chrome est votre meilleur ami.

  1. Mise à jour lors du rechargement : Cochez cette case lors du développement. Cela oblige le navigateur à contourner le cache SW pour le fichier SW lui-même.
  2. Désinscription : option nucléaire. Si les choses sont bizarres, désenregistrez le logiciel pour repartir à zéro.
  3. Cache Storage : affichez exactement quels blobs JSON sont enregistrés. Si api-cache est vide, votre matcher Regex est erroné.

13. Cryptage de la charge utile des notifications push

Web Push est crypté (AES-128-GCM). Le navigateur génère une paire de clés publique/privée. Le serveur (VAPID) doit chiffrer la charge utile avec la clé publique du navigateur. Si vous envoyez du JSON brut, le navigateur le rejette. Le Service Worker reçoit l’événement « push », le déchiffre (géré par le système d’exploitation du navigateur) et affiche la notification. self.registration.showNotification(data.title, { body : data.body, icon : '/icon.png' }). Attendez que la promesse soit résolue (event.waitUntil) pour vous assurer que la notification apparaît même si le logiciel est tué immédiatement.

14. Modules Workbox : utiliser la plateforme

Ne réinventez pas la roue. workbox-precaching, workbox-routing, workbox-strategies, workbox-expiration. Nous utilisons également workbox-window dans le thread principal pour communiquer avec le logiciel. const wb = new Workbox('/sw.js'); wb.addEventListener('installé', ...). Cela supprime l’API complexe navigator.serviceWorker et gère les bizarreries du navigateur (Safari).

15. L’histoire du Web hors ligne (AppCache)

Avant les Service Workers, nous avions AppCache. Il a été défini en 3 mots : “AppCache est un Douchebag”. C’était déclaratif, strict et cassait tout. Si vous avez modifié 1 octet dans le manifeste, le navigateur a TOUT téléchargé à nouveau. Service Workers l’a remplacé par une API Impérative. Vous écrivez du code. Vous décidez quoi faire. Ce passage de la configuration au code est la raison pour laquelle les Service Workers ont réussi là où AppCache a échoué.

16. Les obstacles Safari (iOS)

Apple entretient une relation amour-haine avec les PWA. Ils soutiennent les Service Workers, mais :

  1. Stockage : ils suppriment les données si elles ne sont pas utilisées pendant 7 jours (ITP).
  2. Push : uniquement pris en charge dans iOS 16.4+ (et l’utilisateur DOIT l’ajouter à l’écran d’accueil).
  3. Sync : la synchronisation en arrière-plan est extrêmement limitée. Nous construisons des applications “Progressives”. Signification : Ils fonctionnent parfaitement sur Chrome. Ils se dégradent gracieusement sur Safari (en revenant au réseau uniquement en cas de panne du logiciel).

17. Conclusion

Un Service Worker n’est pas seulement destiné au “Hors ligne”. C’est un outil de performance. Il dissocie l’heure de démarrage de l’application de la qualité du réseau. Sur une connexion 4G irrégulière, un Service Worker fait la différence entre un rebond et une vente.

Chez Maison Code, nous traitons le réseau comme une « infrastructure peu fiable » et intégrons la résilience au client.


**[Engagez nos Architectes](/contact)**.