// apis · Web Platform Advent #4
Service Workers : cache, hors-ligne et mises à jour
Guide pratique des service workers — enregistrement, cycle de vie install/activate, Cache API, servir une page hors-ligne et déployer des mises à jour sans casse.
Un service worker est un script que le navigateur exécute en arrière-plan, indépendamment de votre page. Il se place entre votre application et le réseau comme un proxy programmable : il peut intercepter les requêtes, y répondre depuis un cache et garder votre site fonctionnel même hors-ligne. C'est la base des Progressive Web Apps.
Deux règles incontournables d'abord. Un service worker ne s'exécute qu'en HTTPS (ou sur localhost en développement), et il n'a aucun accès au DOM — il dialogue avec les pages via des événements et des messages, jamais en touchant document.
Enregistrer un service worker
On enregistre le worker depuis le script normal de la page. Vérifiez d'abord le support, puis indiquez le fichier au navigateur :
if ('serviceWorker' in navigator) {
window.addEventListener('load', () => {
navigator.serviceWorker
.register('/sw.js')
.then((reg) => console.log('Scope :', reg.scope))
.catch((err) => console.error('Échec SW :', err));
});
} L'emplacement du fichier compte : un worker à /sw.js contrôle tout l'origine, tandis qu'un worker à /app/sw.js ne contrôle que les pages sous /app/. Cette frontière est sa portée (scope).
Le cycle de vie : install et activate
Un service worker a sa propre vie, indépendante de la page. Les deux événements qui comptent le plus sont install (déclenché une fois, idéal pour pré-cacher les ressources) et activate (idéal pour nettoyer les anciens caches) :
const CACHE = 'site-v1';
const ASSETS = ['/', '/index.html', '/styles.css', '/app.js', '/offline.html'];
self.addEventListener('install', (event) => {
event.waitUntil(
caches.open(CACHE).then((cache) => cache.addAll(ASSETS))
);
});
self.addEventListener('activate', (event) => {
event.waitUntil(
caches.keys().then((keys) =>
Promise.all(keys.filter((k) => k !== CACHE).map((k) => caches.delete(k)))
)
);
}); Appeler event.waitUntil() indique au navigateur de ne pas considérer la phase terminée tant que votre promesse n'est pas résolue — sinon il pourrait détruire le worker en plein travail.
Intercepter les requêtes avec fetch
L'événement fetch est le cœur du système. Vous décidez de ce que renvoie chaque requête. Une stratégie courante et sûre est le cache d'abord, réseau en secours :
self.addEventListener('fetch', (event) => {
event.respondWith(
caches.match(event.request).then((cached) => {
return cached || fetch(event.request);
})
);
}); Pour afficher une page hors-ligne personnalisée quand le cache et le réseau échouent tous deux, ajoutez un catch :
self.addEventListener('fetch', (event) => {
event.respondWith(
fetch(event.request).catch(() => caches.match('/offline.html'))
);
}); Les stratégies de cache en un coup d'œil
| Stratégie | Comportement | Idéale pour |
|---|---|---|
| Cache d'abord | Servir le cache, sinon le réseau | Ressources statiques (CSS, JS, polices) |
| Réseau d'abord | Tenter le réseau, sinon le cache | HTML, données d'API qui changent |
| Stale-while-revalidate | Servir le cache, le mettre à jour en arrière-plan | Avatars, flux, listes de contenu |
Déployer des mises à jour sans casser l'expérience
Quand vous modifiez sw.js, le navigateur détecte les nouveaux octets et installe le nouveau worker, mais il reste en attente tant qu'un onglet utilise encore l'ancien. Cela évite de faire tourner deux versions à la fois. Pour prendre le relais plus tôt, appelez self.skipWaiting() dans install et clients.claim() dans activate — mais seulement si vous êtes sûr que la nouvelle version reste compatible avec les pages déjà ouvertes.
Incrémentez toujours le nom du cache (site-v1 → site-v2) quand les ressources changent. Le gestionnaire activate ci-dessus supprime alors l'ancien cache, et les utilisateurs n'obtiennent jamais un mélange d'anciens et de nouveaux fichiers.
Voilà toute la boucle de base : enregistrer, pré-cacher à l'install, nettoyer à l'activate et répondre aux requêtes au fetch. Commencez par une stratégie cache d'abord pour les fichiers statiques et réseau d'abord pour votre HTML, et vous obtenez un site qui charge instantanément aux visites suivantes et survit à une coupure de connexion.