Développement Web

Progressive Web Apps : applications web installables

18 min de lecture

Ce que vous saurez faire à la fin

  1. Comprendre les avantages PWA vs site classique vs app native.
  2. Configurer un Service Worker pour cache et offline.
  3. Créer un manifeste PWA installable.
  4. Activer push notifications, géolocalisation, caméra.
  5. Tester, déployer et publier sur Play Store via Bubblewrap.

Durée : 2-4 semaines pour PWA complète. Pré-requis : site web HTTPS obligatoire, code source accessible (HTML/CSS/JS ou framework React/Vue/Angular), notions JavaScript, Chrome DevTools, hébergement supportant les Service Workers.

Étape 1 — Comprendre les avantages PWA

Critère Site classique PWA App native
Installation Non Icône écran d’accueil Store
Offline Non Oui (Service Worker) Oui
Push notifications Limité Oui Oui
Coût dev 1,2× 3-5×
App Store Non Oui (TWA) Oui
Mises à jour Auto Auto Via Store
Performance Standard Élevée Maximum

Idéal pour : e-commerce, médias, SaaS B2B, services locaux. Limité pour : jeux 3D, app caméra avancée.

Étape 2 — Vérifier les prérequis techniques

Checklist obligatoire :
✓ HTTPS activé (Let's Encrypt gratuit)
✓ Service Worker enregistrable
✓ Web App Manifest présent
✓ Icônes 192x192 et 512x512 minimum
✓ Responsive design (mobile-first)
✓ Performance Lighthouse > 80

Test : Chrome DevTools → Application → Manifest + Service Workers

Étape 3 — Créer le Web App Manifest

Fichier manifest.json à la racine du site :

{
  "name": "Mon App PWA",
  "short_name": "MonApp",
  "description": "Description courte de l'application",
  "start_url": "/",
  "display": "standalone",
  "background_color": "#ffffff",
  "theme_color": "#2563EB",
  "orientation": "portrait",
  "scope": "/",
  "lang": "fr-SN",
  "icons": [
    {"src": "/icons/icon-192.png", "sizes": "192x192", "type": "image/png", "purpose": "any maskable"},
    {"src": "/icons/icon-512.png", "sizes": "512x512", "type": "image/png", "purpose": "any maskable"}
  ],
  "shortcuts": [
    {"name": "Mon profil", "url": "/profile", "icons": [{"src": "/icons/profile.png", "sizes": "96x96"}]}
  ]
}

Étape 4 — Lier le manifeste dans HTML

<!-- Dans <head> -->
<link rel="manifest" href="/manifest.json">
<meta name="theme-color" content="#2563EB">
<link rel="apple-touch-icon" href="/icons/icon-192.png">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="default">
<meta name="apple-mobile-web-app-title" content="MonApp">

Étape 5 — Créer un Service Worker basique

Fichier service-worker.js à la racine :

const CACHE_NAME = 'monapp-v1';
const ASSETS = [
  '/',
  '/styles.css',
  '/app.js',
  '/icons/icon-192.png',
  '/offline.html'
];

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

self.addEventListener('activate', (event) => {
  event.waitUntil(
    caches.keys().then((keys) =>
      Promise.all(keys.filter(k => k !== CACHE_NAME).map(k => caches.delete(k)))
    )
  );
  self.clients.claim();
});

self.addEventListener('fetch', (event) => {
  event.respondWith(
    caches.match(event.request).then((response) =>
      response || fetch(event.request).catch(() => caches.match('/offline.html'))
    )
  );
});

Étape 6 — Enregistrer le Service Worker

// Dans app.js ou main.js
if ('serviceWorker' in navigator) {
  window.addEventListener('load', () => {
    navigator.serviceWorker.register('/service-worker.js')
      .then((reg) => console.log('SW registered:', reg.scope))
      .catch((err) => console.error('SW registration failed:', err));
  });
}

Étape 7 — Implémenter une stratégie de cache avancée

3 stratégies courantes :

// 1. Cache First (pour assets statiques)
self.addEventListener('fetch', (event) => {
  if (event.request.url.includes('/static/')) {
    event.respondWith(
      caches.match(event.request).then(r => r || fetch(event.request))
    );
  }
});

// 2. Network First (pour API)
async function networkFirst(request) {
  try {
    const response = await fetch(request);
    const cache = await caches.open('api-cache');
    cache.put(request, response.clone());
    return response;
  } catch {
    return caches.match(request);
  }
}

// 3. Stale While Revalidate (pour images dynamiques)
async function staleWhileRevalidate(request) {
  const cache = await caches.open('img-cache');
  const cached = await cache.match(request);
  const fetched = fetch(request).then(r => { cache.put(request, r.clone()); return r; });
  return cached || fetched;
}

Étape 8 — Activer le bouton « Installer l’app »

let deferredPrompt;

window.addEventListener('beforeinstallprompt', (e) => {
  e.preventDefault();
  deferredPrompt = e;
  // Afficher votre bouton "Installer l'app"
  document.getElementById('install-btn').style.display = 'block';
});

document.getElementById('install-btn').addEventListener('click', async () => {
  if (!deferredPrompt) return;
  deferredPrompt.prompt();
  const { outcome } = await deferredPrompt.userChoice;
  console.log(outcome === 'accepted' ? 'Installé' : 'Refusé');
  deferredPrompt = null;
});

Étape 9 — Activer les Push Notifications

// Demander permission
async function subscribeUser() {
  const permission = await Notification.requestPermission();
  if (permission !== 'granted') return;

  const reg = await navigator.serviceWorker.ready;
  const sub = await reg.pushManager.subscribe({
    userVisibleOnly: true,
    applicationServerKey: 'YOUR_VAPID_PUBLIC_KEY'
  });

  // Envoyer la subscription au serveur
  await fetch('/api/save-subscription', {
    method: 'POST',
    body: JSON.stringify(sub),
    headers: {'Content-Type': 'application/json'}
  });
}

// Recevoir notification dans Service Worker
self.addEventListener('push', (event) => {
  const data = event.data.json();
  event.waitUntil(
    self.registration.showNotification(data.title, {
      body: data.body,
      icon: '/icons/icon-192.png',
      badge: '/icons/badge.png',
      data: { url: data.url }
    })
  );
});

self.addEventListener('notificationclick', (event) => {
  event.notification.close();
  event.waitUntil(clients.openWindow(event.notification.data.url));
});

Étape 10 — Accéder caméra, géolocalisation, partage

// Géolocalisation
navigator.geolocation.getCurrentPosition(
  (pos) => console.log(pos.coords),
  (err) => console.error(err)
);

// Caméra
const stream = await navigator.mediaDevices.getUserMedia({ video: true });
document.querySelector('video').srcObject = stream;

// Web Share API (partage natif)
if (navigator.share) {
  await navigator.share({
    title: 'Mon contenu',
    url: 'https://monsite.com/article'
  });
}

// Notifications badging
if ('setAppBadge' in navigator) {
  navigator.setAppBadge(5); // afficher badge "5" sur icône
}

Étape 11 — Auditer avec Lighthouse

Chrome DevTools > Lighthouse > cochez « Progressive Web App » > Run audit. Cibles :

  • Performance > 90
  • Accessibility > 90
  • Best Practices > 90
  • SEO > 90
  • PWA : tous les critères validés (badge « Installable »)

Étape 12 — Publier sur Google Play Store via Bubblewrap

# Installation
npm install -g @bubblewrap/cli

# Initialisation projet (génère un projet Android Studio)
bubblewrap init --manifest=https://votresite.com/manifest.json

# Build APK / AAB
bubblewrap build

# Créez compte Google Play Developer (25 USD one-time)
# Uploadez l'AAB généré
# Décrivez votre app, screenshots, description
# Publication 2-7 jours pour validation

Avantages : votre PWA dans le Play Store, présence officielle, install via Play Store en 1 clic.

Étape 13 — Mesurer l’usage PWA

Google Analytics 4 events à tracker :

// Quand l'app est installée
window.addEventListener('appinstalled', () => {
  gtag('event', 'pwa_installed');
});

// Quand l'app est lancée en mode standalone
if (window.matchMedia('(display-mode: standalone)').matches) {
  gtag('event', 'pwa_launch');
}

// Push notification cliquée
self.addEventListener('notificationclick', () => {
  gtag('event', 'push_clicked');
});

Étape 14 — Maintenir et faire évoluer

Routine mensuelle :

  • Audit Lighthouse (régression performance)
  • Vérifier compatibilité nouveaux navigateurs (Safari iOS notamment, support PWA limité)
  • Update Service Worker (versionner CACHE_NAME pour forcer refresh)
  • Surveiller les KPI : install rate, launch rate, NPS push notifications
  • Itérer sur les features les plus utilisées

Erreurs classiques en PWA

  • Pas de HTTPS : Service Worker refuse de s’enregistrer.
  • Service Worker mal versionné : users coincés sur ancienne version.
  • Cache trop agressif : nouveautés invisibles, frustration.
  • Push notifications spam : users désabonnent, marque ternie.
  • iOS limited support : testez tout sur Safari iOS, surprises possibles.

Checklist PWA complète

✓ HTTPS actif
✓ Manifest JSON complet et lié
✓ Icônes 192 et 512 (any maskable)
✓ Service Worker enregistré
✓ Stratégie de cache (CF, NF, SWR) selon ressource
✓ Page offline.html fallback
✓ Bouton "Installer l'app" actif
✓ Push notifications avec VAPID + backend
✓ Web Share / Camera / Geo si pertinent
✓ Lighthouse PWA score 100
✓ Publié sur Play Store via Bubblewrap
✓ Analytics PWA events trackés
✓ Test régulier Safari iOS

Etape 1 : pourquoi une PWA pour l’Afrique de l’Ouest francophone

Une Progressive Web App (PWA) combine l’experience d’une application native et la portee d’un site web. Pour un editeur a Dakar, Abidjan ou Cotonou, l’enjeu est concret : la majorite du trafic vient de smartphones Android d’entree de gamme avec une connexion 3G intermittente. Une PWA bien construite reste utilisable hors-ligne, s’installe en un clic depuis le navigateur sans passer par le Play Store et pese souvent moins de 500 Ko a l’installation.

Concretement vous obtenez trois benefices que le site classique ne fournit pas : icone sur l’ecran d’accueil, fonctionnement offline avec contenu deja consulte, et notifications push (sur Android et desktop). Le tout sans frais d’editeur Google ni Apple, ce qui est decisif quand on facture en FCFA et que 25 USD de frais Play Store representent environ 16 400 FCFA au taux 1 EUR = 655,957 FCFA via la conversion croisee.

Etape 2 : verifier les prerequis techniques

Avant d’ecrire la moindre ligne, le site doit remplir trois conditions non negociables. Premiere condition, HTTPS actif sur tout le domaine — un certificat Let’s Encrypt gratuit suffit, mais aucun mixed content ne doit subsister. Deuxieme condition, un fichier manifest.webmanifest servi avec le bon Content-Type. Troisieme condition, un service worker enregistre depuis la racine du domaine.

curl -I https://votresite.io/
# Verifie : strict-transport-security present, status 200, http/2

Si le header Strict-Transport-Security est absent, ajoutez-le dans la configuration Nginx ou Apache. Sans HTTPS strict, Chrome refusera d’enregistrer le service worker et l’invite d’installation n’apparaitra jamais.

Etape 3 : ecrire le manifest webmanifest

Le manifest declare le nom de l’app, ses icones, sa couleur de theme et son mode d’affichage. Creez le fichier a la racine publique de votre site.

{
  "name": "Mon App Dakar",
  "short_name": "MonApp",
  "start_url": "/?source=pwa",
  "display": "standalone",
  "background_color": "#0b132b",
  "theme_color": "#1c7c54",
  "icons": [
    { "src": "/icons/icon-192.png", "sizes": "192x192", "type": "image/png" },
    { "src": "/icons/icon-512.png", "sizes": "512x512", "type": "image/png", "purpose": "any maskable" }
  ]
}

Apres deploiement, ouvrez Chrome DevTools onglet Application puis Manifest : la fiche doit s’afficher sans erreur rouge. Si le navigateur signale « icon size mismatch », regenerez vos PNG avec un outil comme sharp ou imagemagick en respectant exactement 192×192 et 512×512 pixels.

Etape 4 : enregistrer un service worker minimal

Le service worker est un script JavaScript qui s’execute en arriere-plan, intercepte les requetes reseau et sert le cache quand la connexion fait defaut. Pour un premier deploiement, gardez-le minimal et stable.

// /sw.js
const CACHE = 'app-v1';
const SHELL = ['/', '/offline.html', '/styles.css', '/app.js'];

self.addEventListener('install', e => {
  e.waitUntil(caches.open(CACHE).then(c => c.addAll(SHELL)));
});

self.addEventListener('fetch', e => {
  e.respondWith(
    fetch(e.request).catch(() => caches.match(e.request).then(r => r || caches.match('/offline.html')))
  );
});

Cote page, ajoutez l’enregistrement dans le footer HTML : if ('serviceWorker' in navigator) navigator.serviceWorker.register('/sw.js');. Apres rechargement, l’onglet Application > Service Workers de DevTools doit afficher « activated and is running ». Tant que ce statut n’est pas vert, l’invite d’installation ne se declenchera pas.

Etape 5 : choisir une strategie de cache adaptee a la 3G

Trois strategies couvrent 90 pourcent des besoins. Cache-first pour les assets statiques (CSS, fonts, logos) qui changent rarement. Network-first avec fallback cache pour les pages HTML qui doivent rester fraiches mais accessibles hors-ligne. Stale-while-revalidate pour les listes d’articles ou un produit catalogue ou la fraicheur n’est pas critique.

Pour un blog ou une boutique a Dakar, la combinaison gagnante est cache-first sur /static/* et stale-while-revalidate sur /api/products. Le visiteur sur Mixx by Yas en zone peri-urbaine voit le contenu en moins de 200 ms meme quand le ping monte a 800 ms, et la mise a jour se fait en silence des qu’une vraie connexion repasse.

Etape 6 : declencher l’invite d’installation

Chrome, Edge et Samsung Internet declenchent l’evenement beforeinstallprompt quand le manifest et le service worker sont valides et que le visiteur a interagi au moins 30 secondes. Capturez l’evenement pour afficher votre propre bouton.

let deferred;
window.addEventListener('beforeinstallprompt', e => {
  e.preventDefault(); deferred = e;
  document.querySelector('#install-btn').hidden = false;
});

document.querySelector('#install-btn').addEventListener('click', async () => {
  if (!deferred) return;
  deferred.prompt();
  const { outcome } = await deferred.userChoice;
  console.log('Resultat installation :', outcome); // 'accepted' ou 'dismissed'
  deferred = null;
});

Sur iOS, le mecanisme est manuel : Safari demande au visiteur d’utiliser le bouton Partager puis « Sur l’ecran d’accueil ». Affichez une infobulle discrete uniquement aux utilisateurs iOS detectes via navigator.userAgent, sinon vous polluez l’experience Android.

Etape 7 : tester avec Lighthouse et corriger les ecarts

Ouvrez Chrome DevTools, onglet Lighthouse, choisissez le profil « Mobile » et le mode « Navigation ». Lancez l’audit. La section PWA doit afficher au moins 90/100 et tous les criteres « Installable » doivent etre verts.

Les trois ecarts les plus frequents en production : icone maskable manquante (ajoutez "purpose": "any maskable" dans le manifest), absence de page /offline.html de fallback (creez un HTML statique de 5 Ko qui explique au visiteur qu’il est hors-ligne), et viewport mal configure (verifiez la balise <meta name="viewport" content="width=device-width, initial-scale=1"> dans le head). Une fois ces points corriges, le score grimpe a 100/100 sans autre intervention.

Etape 8 : surveiller en production et iterer

Installez un script d’analytics qui distingue le mode standalone (PWA installee) du mode browser. La detection se fait avec window.matchMedia('(display-mode: standalone)').matches. Vous saurez ainsi combien d’utilisateurs ont installe l’app et quelle est leur retention a 7 et 30 jours.

Mettez a jour le numero de cache (app-v1 vers app-v2) a chaque deploiement majeur, sinon les visiteurs restent bloques sur l’ancien shell. Pour un suivi avance, lisez le guide mesurer les Core Web Vitals et combinez les metriques avec votre tableau de bord. Pour approfondir sur la couche reseau, consultez aussi le tutoriel optimiser les performances web.

Etape 9 : preparer un plan de bascule pour les anciens visiteurs

Quand vous deployez une refonte majeure du shell, prevoyez un mecanisme de communication avec les service workers deja installes. Dans le nouveau sw.js, ajoutez un self.skipWaiting() dans l’evenement install et un self.clients.claim() dans activate. Le navigateur basculera immediatement les onglets ouverts vers la nouvelle version sans attendre la prochaine visite.

Pensez aussi a versionner le manifest : un changement de name ou start_url casse l’icone deja installee. Si vous renommez l’app, communiquez la migration a Dakar et Abidjan via une banniere visible deux semaines avant, sinon les visiteurs vous percoivent comme une nouvelle app inconnue et desinstallent.

Etape 9 : preparer un plan de bascule pour les anciens visiteurs

Quand vous deployez une refonte majeure du shell, prevoyez un mecanisme de communication avec les service workers deja installes. Dans le nouveau sw.js, ajoutez un self.skipWaiting() dans l’evenement install et un self.clients.claim() dans activate. Le navigateur basculera immediatement les onglets ouverts vers la nouvelle version sans attendre la prochaine visite.

Pensez aussi a versionner le manifest : un changement de name ou start_url casse l’icone deja installee. Si vous renommez l’app, communiquez la migration via une banniere visible deux semaines avant, sinon les visiteurs vous percoivent comme une nouvelle app inconnue et desinstallent au taux 1 EUR = 655,957 FCFA peu importe.

Étape 1 : décider si une PWA est pertinente pour votre projet

Une Progressive Web App (PWA) est un site web qui s’installe sur l’écran d’accueil d’un téléphone, fonctionne hors ligne et envoie des notifications push, sans passer par le Play Store ni l’App Store. Pour un site e-commerce ou média ouest-africain où une partie significative du trafic provient de connexions 3G instables, c’est souvent un meilleur choix qu’une application native — pas de friction d’installation, mise à jour automatique, taille de moins de 1 Mo possible. Pour une application bancaire ou un wallet manipulant de l’argent (Wave, Mixx by Yas), la voie native reste préférable pour des raisons de durcissement.

Validez deux pré-requis avant de coder : votre site doit être servi en HTTPS (Let’s Encrypt suffit), et vous devez maîtriser les bases de JavaScript moderne (ES2020+, async/await, Service Worker API). Si vous partez d’un site WordPress, plusieurs plugins (Super Progressive Web Apps, PWA for WP) couvrent l’essentiel sans coder, mais vous perdez en finesse.

Étape 2 : créer le manifest.webmanifest

Le manifest est un fichier JSON qui décrit votre application au navigateur. Sans lui, le téléphone n’affichera jamais l’invitation « Ajouter à l’écran d’accueil ». Créez le fichier à la racine de votre site.

{
  "name": "Boutique Téranga",
  "short_name": "Téranga",
  "start_url": "/?utm_source=pwa",
  "display": "standalone",
  "background_color": "#ffffff",
  "theme_color": "#0a7c3a",
  "icons": [
    { "src": "/icons/icon-192.png", "sizes": "192x192", "type": "image/png", "purpose": "any maskable" },
    { "src": "/icons/icon-512.png", "sizes": "512x512", "type": "image/png", "purpose": "any maskable" }
  ]
}

Référencez-le dans le <head> de chaque page : <link rel="manifest" href="/manifest.webmanifest">. Vérifiez via DevTools > Application > Manifest qu’aucune erreur n’apparaît. Le bon résultat se reconnaît à : Chrome propose automatiquement « Installer l’app » dans la barre d’URL après quelques secondes.

Étape 3 : enregistrer un Service Worker minimal

Le Service Worker est un script JavaScript qui tourne en arrière-plan, intercepte les requêtes réseau et permet le mode hors ligne. Créez /sw.js à la racine.

// /sw.js
const CACHE = 'teranga-v1';
const ASSETS = ['/', '/style.css', '/app.js', '/offline.html'];

self.addEventListener('install', e => {
  e.waitUntil(caches.open(CACHE).then(c => c.addAll(ASSETS)));
});

self.addEventListener('fetch', e => {
  e.respondWith(
    caches.match(e.request).then(r => r || fetch(e.request).catch(() => caches.match('/offline.html')))
  );
});

Enregistrez-le depuis votre app.js :

if ('serviceWorker' in navigator) {
  navigator.serviceWorker.register('/sw.js').catch(console.error);
}

Rechargez la page deux fois (le SW s’active au second chargement), coupez le wifi de votre laptop, rechargez : la page doit s’afficher depuis le cache. Comment vérifier le bon fonctionnement : DevTools > Application > Service Workers indique « activated and is running ».

Étape 4 : choisir une stratégie de cache adaptée à votre contenu

Le cache « tout statique » de l’étape 3 ne convient pas à un site dynamique. Quatre stratégies courantes, à mixer selon le type de ressource :

// Cache First — pour les assets immuables (CSS hashé, images de produit)
// Network First — pour les pages HTML changeantes
// Stale While Revalidate — pour les listings catégorie
// Network Only — pour les paiements et formulaires sensibles

// Exemple Stale While Revalidate
self.addEventListener('fetch', e => {
  if (e.request.url.includes('/category/')) {
    e.respondWith(
      caches.open(CACHE).then(cache =>
        cache.match(e.request).then(cached => {
          const fresh = fetch(e.request).then(r => { cache.put(e.request, r.clone()); return r; });
          return cached || fresh;
        })
      )
    );
  }
});

Pour ne pas réécrire ce code à la main, utilisez Workbox de Google qui fournit ces patterns en quelques lignes. Vérifiez côté DevTools > Network que les ressources servent bien depuis le SW (colonne Size affiche « ServiceWorker »).

Étape 5 : ajouter les notifications push (avec mesure)

Les notifications push augmentent la rétention mais peuvent vite devenir intrusives. Demandez la permission au bon moment — pas dès l’arrivée sur la page, mais après une action engagée (mise au panier, lecture d’un article entier). Côté serveur, utilisez la spécification Web Push avec une bibliothèque comme web-push (Node.js).

// Côté navigateur, demande de permission contextuelle
document.querySelector('#suivre-prix').addEventListener('click', async () => {
  const reg = await navigator.serviceWorker.ready;
  const sub = await reg.pushManager.subscribe({
    userVisibleOnly: true,
    applicationServerKey: 'VOTRE_VAPID_PUBLIC_KEY'
  });
  await fetch('/api/push/subscribe', { method: 'POST', body: JSON.stringify(sub) });
});

Le bon résultat se reconnaît à : un test de push depuis votre serveur arrive sur le téléphone même quand le navigateur est fermé (sur Android — sur iOS 16.4+ uniquement si l’app a été ajoutée à l’écran d’accueil).

Étape 6 : optimiser la taille pour les connexions 3G

Une PWA qui pèse 4 Mo n’a aucun intérêt en zone rurale ouest-africaine. Visez moins de 250 Ko pour le shell initial (HTML + CSS + JS critique). Activez la compression Brotli côté serveur (Nginx, Cloudflare), supprimez les polyfills inutiles, lazy-load les routes secondaires avec import() dynamique.

# Vérifier le poids réel d'un déploiement
curl -H 'Accept-Encoding: br' -o /dev/null -s -w 'taille: %{size_download} octets\n' https://votre-pwa.sn/

# Auditer le bundle JS avec source-map-explorer
npx source-map-explorer dist/main.*.js

Pour un utilisateur 3G à 200 Ko/s, viser un Time To Interactive sous 5 secondes au premier chargement et sous 1 seconde aux visites suivantes. Mesurez via Lighthouse > Performance > « Slow 4G » throttling.

Étape 7 : tester sur de vrais téléphones d’entrée de gamme

Tester uniquement dans Chrome desktop ne reflète pas l’expérience d’un Tecno Spark ou d’un Itel à 50 000 FCFA (environ 76,22 EUR au taux fixe 1 EUR = 655,957 FCFA). Achetez ou empruntez un appareil représentatif de votre cible, ou à défaut utilisez le throttling CPU de DevTools (4x ou 6x slowdown).

# Lighthouse en mode "low-end mobile" simulé
npx lighthouse https://votre-pwa.sn \
  --emulated-form-factor=mobile \
  --throttling.cpuSlowdownMultiplier=6 \
  --throttling.requestLatencyMs=300 \
  --output=html --output-path=./report-low-end.html

Vérifiez aussi le comportement sans wifi : activez le mode avion sur le vrai téléphone et naviguez. Si la page reste utilisable (lecture du dernier contenu visité), c’est gagné.

Étape 8 : déployer et mesurer l’adoption

Hébergez votre PWA sur un CDN (Cloudflare Pages gratuit, Netlify, Vercel) ou un VPS bien configuré. Surveillez quatre indicateurs dans la durée : taux d’installation (événements « appinstalled »), taux de revisite hors ligne, opt-in notifications, taux de désabonnement. Logguez-les côté serveur via un endpoint /metrics simple.

Mettez à jour le Service Worker avec un numéro de version explicite (const CACHE = 'teranga-v2') à chaque déploiement majeur, sinon les utilisateurs gardent l’ancienne version pendant des semaines.

Dans la continuité, lisez notre tutoriel sur l’audit performance Lighthouse en profondeur, et pour la dimension hébergement le déploiement statique sur Cloudflare Pages.

Partager