Qu’est-ce qu’une PWA ?
Une Progressive Web App est un site web qui se comporte comme une application installée : elle fonctionne hors ligne, se charge instantanément lors des visites répétées et peut être ajoutée à l’écran d’accueil ou au bureau. Il n’existe pas de technologie unique appelée PWA — c’est un ensemble de fonctionnalités que vous adoptez progressivement.
Trois ingrédients permettent son fonctionnement :
- HTTPS, pour que le navigateur fasse suffisamment confiance à votre origine pour autoriser un service worker.
- Un web manifest qui décrit le nom, les icônes et le mode d’affichage de l’application.
- Un service worker, un script d’arrière-plan qui intercepte les requêtes réseau et gère les caches.
Le résultat est une expérience qui semble native tout en restant un site web que vous pouvez mettre à jour instantanément.
Le manifeste web
Le manifeste est un fichier JSON lié depuis le head HTML. Il indique à la plateforme comment présenter l’application une fois installée.
{
"name": "hackweb Reader",
"short_name": "Reader",
"start_url": "/",
"display": "standalone",
"background_color": "#0b0d0c",
"theme_color": "#0b0d0c",
"icons": [
{ "src": "/icons/192.png", "sizes": "192x192", "type": "image/png" },
{ "src": "/icons/512.png", "sizes": "512x512", "type": "image/png" }
]
}
<!-- index.html -->
<link rel="manifest" href="/manifest.webmanifest" />
<meta name="theme-color" content="#0b0d0c" />
display: "standalone" masque l’interface du navigateur (le “chrome”) lors de l’installation, tandis que les icônes et les couleurs contrôlent l’écran de démarrage (splash screen) et le sélecteur de tâches. Un manifeste valide associé à un service worker est ce qui permet au navigateur de proposer l’installation de l’application.
Le cycle de vie du service worker
Un service worker est un script distinct qui n’a pas accès au DOM. Il possède un cycle de vie spécifique : install, activate et fetch.
// register.js
if ("serviceWorker" in navigator) {
window.addEventListener("load", () => {
navigator.serviceWorker.register("/sw.js");
});
}
Pendant la phase install, vous pré-mettez en cache le “app shell”. Pendant la phase activate, vous nettoyez les anciens caches. Pendant la phase fetch, vous interceptez les requêtes et décidez de la manière d’y répondre. Un nouveau worker attend que toutes les pages utilisant l’ancien soient fermées avant de s’activer, c’est pourquoi les mises à jour nécessitent parfois un second rechargement.
Stratégies de mise en cache
Le cœur d’un service worker réside dans sa stratégie de mise en cache. Différentes ressources nécessitent des traitements différents.
- Cache-first — sert depuis le cache, puis se rabat sur le réseau. Idéal pour les assets versionnés et immuables, comme les bundles hachés.
- Network-first — tente d’abord le réseau, puis se rabat sur le cache. Adapté pour le HTML et les données qui doivent être récentes.
- Stale-while-revalidate — sert immédiatement la version mise en cache et la met à jour en arrière-plan. Excellent pour les données où un léger retard est acceptable.
- Cache-only et network-only — pour des cas d’utilisation très spécifiques.
// strategies.js
self.addEventListener("fetch", (event) => {
const { request } = event;
if (request.destination === "image") {
event.respondWith(
caches.match(request).then((cached) => cached || fetch(request)),
);
}
});
La propriété request.destination vous indique quel type de ressource est récupéré, ce qui vous permet d’appliquer une stratégie par type d’asset.
Repli hors ligne (Offline fallback)
Même avec la mise en cache, certaines requêtes échoueront. Prévoyez un mécanisme de repli élégant.
// fallback.js
self.addEventListener("fetch", (event) => {
if (event.request.mode === "navigate") {
event.respondWith(
fetch(event.request).catch(() => caches.match("/offline.html")),
);
}
});
Une page hors ligne, un shell mis en cache ou un état “vous êtes hors ligne” explicite sont bien préférables à l’erreur par défaut du navigateur. Combiné avec la synchronisation en arrière-plan (background sync), les requêtes ayant échoué hors ligne peuvent être retentées automatiquement dès que la connectivité est rétablie.
Mises à jour et versionnage
Le caching est puissant, mais facile à mal implémenter. La règle est simple : versionnez vos caches et supprimez les anciens lors de l’activation. Sinon, les utilisateurs risquent de rester bloqués sur des fichiers obsolètes indéfiniment.
// activate.js
const CACHE = "app-v2";
self.addEventListener("activate", (event) => {
event.waitUntil(
caches.keys().then((keys) =>
Promise.all(keys.filter((k) => k !== CACHE).map((k) => caches.delete(k))),
),
);
});
Pour tout ce qui dépasse les bases, la bibliothèque Workbox gère le precaching, le routage, l’expiration et le nettoyage avec des configurations par défaut éprouvées, et s’intègre à la plupart des outils de build.
Au-delà du mode hors ligne
Les service workers permettent bien plus que le simple caching :
- Background sync relance les requêtes ayant échoué dès que le réseau est rétabli.
- Push notifications délivrent des messages même lorsque l’application est fermée.
- Periodic sync actualise le contenu en arrière-plan.
- Share target permet à votre application de recevoir du contenu partagé depuis l’OS.
Chacune de ces fonctionnalités nécessite l’autorisation de l’utilisateur et doit être utilisée avec parcimonie, mais c’est précisément ce qui donne à une PWA l’aspect d’une application native.
Bonnes pratiques
- Servez vos contenus via HTTPS et enregistrez le worker après le chargement.
- Versionnez vos caches et supprimez les anciens lors de l’activation.
- Utilisez une stratégie cache-first pour les assets hachés et network-first pour le HTML.
- Prévoyez systématiquement une page de secours (fallback) pour le mode hors ligne.
- Ne pré-mettez en cache que l’app shell, et non l’intégralité du site.
- Envisagez d’utiliser Workbox plutôt que d’écrire manuellement des stratégies complexes.
- Testez le mode hors ligne et les mises à jour avant le déploiement.
Erreurs courantes
- Mettre en cache le HTML indéfiniment et servir des pages obsolètes.
- Oublier de purger les anciens caches après un déploiement.
- Mettre en cache des réponses cross-origin opaques sans en comprendre les limites.
- Tout mettre en pré-cache, ce qui alourdit la première installation.
- Supposer que le nouveau worker s’active immédiatement.
- Ignorer le quota de stockage et laisser les caches croître sans limite.
Et après ?
Un service worker est le levier de performance le plus important pour les visites récurrentes et constitue le socle du support hors ligne. Combinez-le avec les techniques du guide sur la Web Performance, comprenez les requêtes HTTP qu’il intercepte et servez votre application depuis un backend Node.js. Ensuite, ajoutez un manifest et un worker simple avec une stratégie “cache-first” à un site existant pour le voir fonctionner hors ligne.