Service workers et mise en cache avancée pour vos PWA
Les Progressive Web Apps transforment la manière dont les utilisateurs interagissent avec le web, en combinant la portabilité d'un site et la richesse d'une application native. Au cœur de cette révolution silencieuse se trouvent les service workers, des scripts JavaScript qui s'exécutent en arrière-plan, indépendamment de la page affichée. Ils constituent le socle technique qui permet le fonctionnement hors ligne, les notifications push et la synchronisation différée.
La mise en cache représente le levier principal pour offrir une expérience fluide, même lorsque la connexion réseau est instable ou absente. Bien configurée, elle réduit le temps de chargement perçu, diminue la consommation de bande passante et améliore le score Core Web Vitals. À l'inverse, une stratégie mal pensée peut servir des ressources obsolètes ou gonfler inutilement le stockage du navigateur, dégradant ainsi la réactivité globale.
Ce guide explore les différentes stratégies applicables dans une PWA, depuis les patterns les plus simples jusqu'aux architectures hybrides. Vous y trouverez des exemples concrets, des bonnes pratiques de cycle de vie et des pistes pour valider votre implémentation à l'aide d'outils spécialisés comme ce comparatif des frameworks de test Node.js.
Le rôle des service workers dans l'écosystème PWA
Un service worker agit comme un proxy programmable entre le navigateur et le réseau. En interceptant chaque requête sortante, il peut décider de servir une réponse depuis le cache, de la récupérer en ligne, ou de combiner les deux approches. Cette polyvalence ouvre la porte à des scénarios sophistiqués : préchargement de pages, mise à jour silencieuse de ressources, gestion fine des quotas de stockage.
Pour s'enregistrer, le worker doit être servi depuis une origine sécurisée en HTTPS et respecter une scope précise. Le fichier JavaScript correspondant est téléchargé par le navigateur, qui déclenche un cycle d'installation, d'activation, puis de prise de contrôle des requêtes. Une fois actif, il continue de vivre même après la fermeture de l'onglet, ce qui en fait un candidat idéal pour des tâches différées.
L'une des erreurs fréquentes consiste à confondre service worker et Web Worker. Le premier intercepte le trafic réseau et possède sa propre API asynchrone ; le second se contente d'exécuter du JavaScript dans un thread séparé pour alléger le thread principal. Les deux peuvent coexister, mais leurs API diffèrent sensiblement et répondent à des besoins distincts.
Stratégies de cache fondamentales
La stratégie cache-first privilégie la rapidité en servant systématiquement la ressource depuis le cache local. Elle convient aux fichiers statiques versionnés (JS, CSS, polices) qui changent rarement. Son principal risque : renvoyer une version obsolète si la mise à jour n'a pas été propagée correctement à tous les utilisateurs.
À l'opposé, le pattern network-first tente systématiquement de récupérer la ressource fraîche sur le réseau, puis bascule vers le cache en cas d'échec. C'est la stratégie privilégiée pour les API JSON, où la fraîcheur des données importe davantage que la latence. Elle garantit une expérience cohérente avec le mode en ligne, tout en conservant un fallback en cas de coupure.
Le pattern stale-while-revalidate combine les deux : il sert immédiatement la version cachée, puis déclenche en parallèle une requête réseau pour mettre à jour le cache. L'utilisateur perçoit une réponse instantanée, tout en bénéficiant d'un contenu qui se rafraîchit au fil des visites. C'est souvent le compromis idéal pour des contenus éditoriaux ou des listes de produits.
Implémenter un cache-first robuste avec Workbox
La librairie Workbox, maintenue par Google, simplifie considérablement l'écriture de la logique de cache. Elle propose des modules prêts à l'emploi pour le routage, les stratégies, la précharge et la gestion des quotas. Pour un projet React ou Vue, son intégration se fait en quelques lignes de configuration au sein du bundler.
L'approche consiste à définir une liste de routes avec une stratégie associée. Par exemple, les images décoratives peuvent être traitées en CacheFirst avec une expiration maximale de trente jours, tandis que les réponses d'API suivent une logique StaleWhileRevalidate. Le fichier sw.js généré peut ensuite être enrichi avec des plugins de télémétrie ou de diffusion vers les clients.
Pour les projets qui manipulent des données structurées via un ORM, il est utile de s'appuyer sur des comparatifs spécialisés. L'article sur Prisma vs Drizzle vs TypeORM offre un éclairage pertinent sur la couche d'accès aux données, complémentaire à la stratégie de cache côté client.
Stale-while-revalidate et expérience perçue
La stratégie stale-while-revalidate mérite une attention particulière car elle est souvent la plus équilibrée. Son implémentation manuelle tient en quelques lignes : récupérer la version cachée, répondre immédiatement, puis lancer une requête réseau et mettre à jour le cache. Cette technique réduit drastiquement le temps d'interaction perçu par l'utilisateur.
Pour les visiteurs en ligne, l'expérience est identique à un cache miss classique, puisque la requête de mise à jour est asynchrone. Pour les utilisateurs hors ligne, le contenu déjà présent dans le cache reste accessible sans dégradation. Le défi consiste à choisir une politique d'expiration cohérente avec la fréquence de mise à jour réelle des données métier.
Un piège classique consiste à oublier de gérer les erreurs réseau. Si la requête de revalidation échoue, le worker doit simplement conserver l'entrée existante sans avertir l'utilisateur. Une bonne pratique consiste à journaliser ces échecs via un point de télémétrie, afin de détecter les pics d'incidents et d'adapter la stratégie en conséquence.
Gestion du stockage : quota, éviction et IndexedDB
Le navigateur impose un quota de stockage partagé entre toutes les origines d'un même site. Par défaut, il alloue plusieurs centaines de mégaoctets, mais ce volume peut varier selon l'espace disponible sur l'appareil. Le service worker doit surveiller cet espace et appliquer des règles d'éviction intelligentes pour éviter les erreurs quota exceeded.
L'API Cache Storage suffit pour les requêtes HTTP classiques (GET, HEAD). Pour des données plus structurées, IndexedDB reste incontournable. Elle permet de stocker des objets JavaScript arbitraires, avec des index secondaires pour des recherches performantes. Combiner les deux stockages offre une flexibilité maximale, chacun répondant à un usage précis.
La stratégie LRU intégrée à Cache Storage permet d'évictionner automatiquement les entrées les plus anciennes lorsque le quota est atteint. Pour IndexedDB, il faut implémenter sa propre logique de purge, en s'appuyant sur des horodatages et des seuils configurables depuis une console d'administration.
Cycle de vie et mise à jour des workers
Le cycle de vie d'un service worker comporte plusieurs phases : installation, attente, activation et contrôle des pages. Comprendre ces étapes est essentiel pour éviter les bugs de mise à jour. Lorsqu'une nouvelle version est détectée, le navigateur attend que toutes les pages contrôlées par l'ancien worker soient fermées avant de l'activer.
La méthode skipWaiting() permet de forcer l'activation immédiate, mais elle peut entraîner des incohérences si certaines pages utilisent encore l'ancienne version. Une approche plus douce consiste à informer l'utilisateur via un message broadcast et à lui proposer de recharger la page au moment opportun, par exemple à la prochaine navigation.
Côté serveur, il est recommandé d'utiliser un mécanisme de versionning explicite, qu'il s'agisse d'un commentaire dans le fichier ou d'un en-tête HTTP dédié, et de désactiver le cache HTTP pour le script du worker lui-même. Sans cela, le navigateur risque de servir une version obsolète du worker et de ne jamais déclencher la chaîne de mise à jour.
Tester et déboguer la logique de cache
Le panneau Application des DevTools Chrome offre une vue complète sur les service workers actifs, les caches présents et l'espace utilisé. L'onglet Bypass for network permet de tester le comportement hors ligne en simulant une coupure réseau. Les options Update on reload et Unregister facilitent les cycles de développement itératif sans redémarrage manuel.
Pour les tests automatisés, Workbox propose des utilitaires Node qui permettent de valider la configuration sans navigateur. Pour des tests d'intégration plus poussés, des frameworks comme Playwright offrent un contrôle fin sur l'état du réseau et le cycle de vie des workers. Les scénarios à couvrir incluent le premier chargement, la navigation hors ligne, le retour en ligne, la mise à jour du worker et le dépassement de quota.
Bonnes pratiques à appliquer dès le départ :
- Adopter la stratégie stale-while-revalidate pour les contenus éditoriaux et les listes de données fréquemment consultées
- Réserver cache-first aux ressources versionnées (JS, CSS, polices) et network-first aux endpoints critiques
- Versionner explicitement le fichier du service worker et désactiver son cache HTTP côté serveur
- Surveiller le quota de stockage et implémenter une logique d'éviction pour éviter les erreurs quota exceeded
- Mettre en place une télémétrie légère pour tracer les fallbacks réseau et les évictions de cache
Pour aller plus loin dans l'industrialisation de vos PWA, notre zone d'intégration rassemble des outils, des connecteurs et des bonnes pratiques validés par la communauté. Vous y trouverez des composants prêts à l'emploi pour accélérer le déploiement de vos workers et harmoniser vos stratégies de cache à l'échelle d'un parc d'applications.