Angular PWA : Service Worker et cache offline

🏷️ Front-end 📅 14/04/2026 01:35:00 👤 Mezgani said
Angular Pwa Service Worker Offline Performance
Angular PWA : Service Worker et cache offline

Transformez votre app Angular en PWA performante : service worker, stratégies de cache, mode offline et mise à jour sans friction.

Activer le Service Worker Angular

Angular fournit le package @angular/service-worker qui génère un service worker optimisé à partir d'un fichier de configuration JSON — pas besoin d'écrire le worker à la main.

ng add @angular/pwa

Le CLI crée automatiquement :

  • ngsw-config.json — configuration du cache et des stratégies.
  • manifest.webmanifest — icônes, couleurs et nom pour l'installation sur l'écran d'accueil.
  • L'enregistrement du service worker dans app.config.ts.
// app.config.ts (Angular 17+ standalone)
import { provideServiceWorker } from '@angular/service-worker';
import { isDevMode } from '@angular/core';

export const appConfig: ApplicationConfig = {
    providers: [
        provideServiceWorker('ngsw-worker.js', {
            enabled: !isDevMode(),          // désactivé en dev
            registrationStrategy: 'registerWhenStable:30000'
        })
    ]
};
Important: les Service Workers ne fonctionnent qu'en HTTPS (ou localhost). N'oublie pas de tester le build de production avec ng build + http-server dist/ — jamais ng serve qui désactive le SW.

Configuration ngsw-config.json

Le fichier ngsw-config.json déclare quels assets mettre en cache, avec quelle stratégie, et quelles API intercepter.

{
  "$schema": "./node_modules/@angular/service-worker/config/schema.json",
  "index": "/index.html",
  "assetGroups": [
    {
      "name": "app-shell",
      "installMode": "prefetch",
      "resources": {
        "files": ["/favicon.ico", "/index.html", "/manifest.webmanifest",
                  "/*.css", "/*.js"]
      }
    },
    {
      "name": "assets",
      "installMode": "lazy",
      "updateMode": "prefetch",
      "resources": {
        "files": ["/assets/**", "/*.(svg|cur|jpg|jpeg|png|apng|webp|gif|otf|ttf|woff|woff2)"]
      }
    }
  ],
  "dataGroups": [
    {
      "name": "api-freshness",
      "urls": ["/api/articles"],
      "cacheConfig": {
        "strategy": "freshness",
        "maxSize": 100,
        "maxAge": "3d",
        "timeout": "10s"
      }
    }
  ]
}

installMode vs updateMode

  1. prefetch — télécharge les ressources immédiatement lors de l'installation du SW. Idéal pour le shell applicatif.
  2. lazy — télécharge uniquement quand la ressource est demandée la première fois. Idéal pour les images et assets secondaires.

Stratégies de cache

Pour les dataGroups (appels API), deux stratégies principales s'opposent :

  • freshness — essaie le réseau en premier, utilise le cache si le réseau ne répond pas dans le timeout. Idéal pour les données qui changent fréquemment (flux, prix, stocks).
  • performance — utilise le cache en premier, interroge le réseau uniquement si la donnée est expirée (maxAge). Idéal pour les données stables (config, catalogue, profil).
{
  "dataGroups": [
    {
      "name": "api-config",
      "urls": ["/api/config", "/api/categories"],
      "cacheConfig": {
        "strategy": "performance",
        "maxSize": 20,
        "maxAge": "1d"
      }
    },
    {
      "name": "api-live",
      "urls": ["/api/prices/**"],
      "cacheConfig": {
        "strategy": "freshness",
        "maxSize": 50,
        "maxAge": "1m",
        "timeout": "5s"
      }
    }
  ]
}

Mode offline et fallback

Quand l'utilisateur perd la connexion, le service worker sert les assets depuis le cache. Pour les routes dynamiques sans cache, il faut prévoir un fallback explicite.

// Détecter l'état de connexion dans l'application
import { Injectable, signal } from '@angular/core';

@Injectable({ providedIn: 'root' })
export class ConnectivityService {
    readonly isOnline = signal(navigator.onLine);

    constructor() {
        window.addEventListener('online',  () => this.isOnline.set(true));
        window.addEventListener('offline', () => this.isOnline.set(false));
    }
}

// Dans un composant
@Component({
    template: `
        @if (!connectivity.isOnline()) {
            <div class="alert alert-warning" role="alert">
                Vous êtes hors ligne. Certaines données peuvent ne pas être à jour.
            </div>
        }
    `
})
export class AppComponent {
    connectivity = inject(ConnectivityService);
}
Page offline dédiée: ajoute une route /offline avec un composant simplifié qui s'affiche quand une navigation vers une page non cachée échoue. Configure-la dans ngsw-config.json comme fallback de navigation.

Gestion des mises à jour

Quand un nouveau build est déployé, le service worker le détecte en arrière-plan. Angular expose le service SwUpdate pour informer l'utilisateur et appliquer la mise à jour au bon moment.

import { Component, inject, OnInit } from '@angular/core';
import { SwUpdate, VersionReadyEvent } from '@angular/service-worker';
import { filter } from 'rxjs/operators';

@Component({ ... })
export class AppComponent implements OnInit {
    private swUpdate = inject(SwUpdate);

    ngOnInit(): void {
        if (!this.swUpdate.isEnabled) return;

        // Écouter quand une nouvelle version est prête
        this.swUpdate.versionUpdates.pipe(
            filter((evt): evt is VersionReadyEvent => evt.type === 'VERSION_READY')
        ).subscribe(() => {
            if (confirm('Une nouvelle version est disponible. Recharger ?')) {
                this.swUpdate.activateUpdate().then(() =>
                    document.location.reload()
                );
            }
        });

        // Vérifier manuellement (ex: au focus de l'onglet)
        document.addEventListener('visibilitychange', () => {
            if (!document.hidden) this.swUpdate.checkForUpdate();
        });
    }
}

Checklist PWA

  • Activer le SW uniquement en production avec enabled: !isDevMode() — évite les conflits de cache en développement.
  • Tester avec ng build + serveur local HTTPS — jamais avec ng serve.
  • Configurer manifest.webmanifest : icônes 192px et 512px, display: standalone, theme_color.
  • Utiliser installMode: prefetch pour le shell et lazy pour les assets secondaires.
  • Choisir la stratégie freshness pour les API temps réel et performance pour les données stables.
  • Implémenter un banner de mise à jour avec SwUpdate.versionUpdates — ne pas forcer le rechargement sans confirmation.
  • Afficher un message clair quand l'utilisateur est hors ligne via navigator.onLine et les événements online/offline.
  • Auditer avec Lighthouse (score PWA) après le build de production — viser 90+ sur mobile.