Partager via


Utiliser un service Worker pour gérer les demandes réseau

Un worker de service est un type spécial de web worker capable d’intercepter, de modifier et de répondre aux requêtes réseau à l’aide de l’API Fetch . Pour stocker des ressources, un worker de service peut accéder à l’API Cache et accéder à des magasins de données côté client asynchrones, tels que IndexedDB.

Un service Worker peut accélérer votre PWA en mettant en cache les ressources localement, et peut également rendre votre PWA plus fiable en lui permettant de fonctionner même lorsque l’appareil de l’utilisateur est hors connexion ou dispose d’une connexion réseau intermittente.

Si votre PWA dispose d’un service Worker, celui-ci est installé la première fois que l’utilisateur accède à votre PWA. Le service Worker s’exécute ensuite en parallèle avec votre application et peut continuer à travailler même lorsque votre application n’est pas en cours d’exécution.

Un worker de service est responsable de l’interception, de la modification et de la réponse aux demandes réseau. Le service Worker peut être alerté lorsque l’application tente de charger une ressource à partir du serveur, ou quand l’application envoie une demande d’obtention de données à partir du serveur. Dans ce cas, le worker de service peut décider de laisser la demande accéder au serveur, ou d’intercepter la requête et de retourner une réponse à partir du cache à la place.

Diagramme montrant le service Worker entre l’application et le stockage réseau et cache

Inscrire un worker de service

Comme pour les autres workers web, un worker de service doit exister dans un fichier distinct. Le fichier contient un seul worker de service. Vous référencez ce fichier lors de l’inscription du service Worker, comme indiqué dans le code suivant :

if ("serviceWorker" in navigator) {
  navigator.serviceWorker.register("/serviceworker.js");
}

Le navigateur web qui exécute votre PWA peut fournir différents niveaux de prise en charge pour votre service Worker. En outre, le contexte dans lequel votre PWA s’exécute peut ne pas être sécurisé. Par conséquent, il est recommandé de tester l’existence de l’objet navigator.serviceWorker avant d’exécuter tout code lié au worker de service. Dans le code ci-dessus, un worker de service est inscrit à l’aide du serviceworker.js fichier situé à la racine du site.

Veillez à placer le fichier service Worker dans le répertoire de niveau le plus élevé que vous souhaitez que le service Worker gère. Un tel répertoire est appelé l’étendue du service Worker. Dans le code précédent, le fichier est stocké dans le répertoire racine de votre application, et le service Worker gère toutes les pages qui se trouvent sous le nom de domaine de l’application.

Si le fichier de service Worker a été stocké dans un js répertoire, l’étendue du service Worker est limitée au js répertoire et à tous les sous-répertoires. En guise de meilleure pratique, placez le fichier worker de service à la racine de votre application, sauf si vous avez besoin de réduire l’étendue de votre service Worker.

Intercepter les demandes

L’événement principal que vous utilisez dans un worker de service est l’événement fetch . L’événement fetch s’exécute chaque fois que le navigateur exécuté par votre application tente d’accéder au contenu dans l’étendue du service Worker.

Le code suivant montre comment ajouter un écouteur pour l’événement fetch :

self.addEventListener("fetch", event => {
  console.log("Fetching", event.request);
});

Dans le fetch gestionnaire, vous pouvez contrôler si une requête est envoyée au réseau, si elle est extraite du cache, etc. L’approche que vous adopterez varie probablement en fonction du type de ressource demandée, de la fréquence à laquelle elle est mise à jour et d’autres logiques métier propres à votre application.

Voici quelques exemples de ce que vous pouvez faire dans le fetch gestionnaire :

  • Si disponible, retournez une réponse à partir du cache ; sinon, demandez la ressource sur le réseau.
  • Récupérez une ressource à partir du réseau, mettez en cache une copie et retournez la réponse.
  • Autoriser les utilisateurs à spécifier une préférence pour enregistrer les données.
  • Fournissez une image d’espace réservé pour certaines demandes d’image.
  • Générez une réponse directement dans le service Worker.

Cycle de vie du service worker

Le cycle de vie d’un worker de service se compose de plusieurs étapes, chaque étape déclenchant un événement. Vous pouvez ajouter des écouteurs à ces événements pour exécuter du code afin d’effectuer une action. La liste suivante présente une vue d’ensemble du cycle de vie et des événements associés d’un worker de service :

  1. Inscrivez le service Worker.

  2. Le navigateur télécharge le fichier JavaScript, installe le service Worker et déclenche l’événement install . Vous pouvez utiliser l’événement install pour pré-mettre en cache tous les fichiers importants et de longue durée (tels que des fichiers CSS, des fichiers JavaScript, des images de logo ou des pages hors connexion) à partir de votre application.

    self.addEventListener("install", event => {
      console.log("Install event in progress.");
    });
    
  3. Le service Worker est activé, ce qui déclenche l’événement activate . Utilisez cet événement pour nettoyer les caches obsolètes.

    self.addEventListener("activate", event => {
      console.log("Activate event in progress.");
    });
    
  4. Le service Worker est prêt à s’exécuter lorsque la page est actualisée ou lorsque l’utilisateur accède à une nouvelle page sur le site. Si vous souhaitez exécuter le service Worker sans attendre, utilisez la self.skipWaiting() méthode pendant l’événement install , comme suit :

    self.addEventListener("install", event => {
      self.skipWaiting();
    });
    
  5. Le service Worker est maintenant en cours d’exécution et peut écouter fetch les événements.

Ressources de pré-mise en cache

Lorsqu’un utilisateur accède à votre application pour la première fois, si vous avez défini un worker de service, celui-ci est installé. Utilisez l’événement install dans votre code de travail de service pour détecter le moment où cela se produit et mettre en cache toutes les ressources statiques dont votre application a besoin. La mise en cache des ressources statiques de votre application (telles que le code HTML, CSS et JavaScript) nécessaires à la page de démarrage permet à votre application de s’exécuter même lorsque l’appareil de l’utilisateur est hors connexion.

Pour mettre en cache les ressources de votre application, utilisez l’objet global caches et la cache.addAll méthode, comme indiqué ci-dessous :

// The name of the cache your app uses.
const CACHE_NAME = "my-app-cache";
// The list of static files your app needs to start.
const PRE_CACHED_RESOURCES = ["/", "styles.css", "app.js"];

// Listen to the `install` event.
self.addEventListener("install", event => {
  async function preCacheResources() {
    // Open the app's cache.
    const cache = await caches.open(CACHE_NAME);
    // Cache all static resources.
    cache.addAll(PRE_CACHED_RESOURCES);
  }

  event.waitUntil(preCacheResources());
});

Notez qu’après l’installation initiale, l’événement install ne s’exécute plus. Pour mettre à jour le code de votre service Worker, consultez Mettre à jour votre service Worker.

Vous pouvez maintenant utiliser l’événement fetch pour retourner les ressources statiques à partir du cache, au lieu de les charger à nouveau à partir du réseau :

self.addEventListener("fetch", event => {
  async function returnCachedResource() {
    // Open the app's cache.
    const cache = await caches.open(CACHE_NAME);
    // Find the response that was pre-cached during the `install` event.
    const cachedResponse = await cache.match(event.request.url);

    if (cachedResponse) {
      // Return the resource.
      return cachedResponse;
    } else {
      // The resource wasn't found in the cache, so fetch it from the network.
      const fetchResponse = await fetch(event.request.url);
      // Put the response in cache.
      cache.put(event.request.url, fetchResponse.clone());
      // And return the response.
      return fetchResponse.
    }
  }

  event.respondWith(returnCachedResource());
});

Par souci de concision, l’exemple de code ci-dessus ne gère pas les cas où l’obtention de l’URL de la requête à partir du réseau a échoué.

Utiliser une page hors connexion personnalisée

Lorsque votre application utilise plusieurs pages HTML, un scénario courant en mode hors connexion consiste à rediriger les demandes de navigation vers une page d’erreur personnalisée lorsque l’appareil de l’utilisateur est hors connexion :

// The name of the cache your app uses.
const CACHE_NAME = "my-app-cache";
// The list of static files your app needs to start.
// Note the offline page in this list.
const PRE_CACHED_RESOURCES = ["/", "styles.css", "app.js", "/offline"];

// Listen to the `install` event.
self.addEventListener("install", event => {
  async function preCacheResources() {
    // Open the app's cache.
    const cache = await caches.open(CACHE_NAME);
    // Cache all static resources.
    cache.addAll(PRE_CACHED_RESOURCES);
  }

  event.waitUntil(preCacheResources());
});

self.addEventListener("fetch", event => {
  async function navigateOrDisplayOfflinePage() {
    try {
      // Try to load the page from the network.
      const networkResponse = await fetch(event.request);
      return networkResponse;
    } catch (error) {
      // The network call failed, the device is offline.
      const cache = await caches.open(CACHE_NAME);
      const cachedResponse = await cache.match("/offline");
      return cachedResponse;
    }
  }

  // Only call event.respondWith() if this is a navigation request
  // for an HTML page.
  if (event.request.mode === 'navigate') {
    event.respondWith(navigateOrDisplayOfflinePage());
  }
});

Mettre à jour votre service Worker

Installer une nouvelle version de Service Worker

Si vous apportez des modifications au code de votre service Worker et déployez le nouveau fichier service Worker sur votre serveur web, les appareils de vos utilisateurs commenceront progressivement à utiliser le nouveau service Worker.

Chaque fois qu’un utilisateur accède à l’une des pages de votre application, le navigateur qui exécute l’application vérifie si une nouvelle version du service Worker est disponible sur le serveur. Le navigateur détecte les nouvelles versions en comparant le contenu entre le worker de service existant et le nouveau worker de service. Lorsqu’une modification est détectée, le nouveau worker de service est installé (son install événement est déclenché), puis le nouveau service worker attend que le service worker existant cesse d’être utilisé sur l’appareil.

Dans la pratique, cela signifie qu’il peut y avoir deux workers de service en cours d’exécution en même temps, mais seul le worker de service existant (d’origine) intercepte les requêtes réseau de l’application. Lorsque l’application est fermée, le service Worker existant cesse d’être utilisé. La prochaine fois que l’application est ouverte, le nouveau service Worker est activé. L’événement activate est déclenché et le nouveau worker de service commence à intercepter les fetch événements.

Vous pouvez activer de force le nouveau service Worker dès qu’il est installé, en utilisant self.skipWaiting() dans le gestionnaire d’événements de install votre service Worker.

Pour en savoir plus sur la façon dont un Service Worker est mis à jour, consultez Mise à jour du Service Worker sur web.dev.

Mettre à jour vos fichiers statiques mis en cache

Lors de la pré-mise en cache de ressources statiques telles que les fichiers de feuille de style CSS, comme décrit dans Ressources de pré-mise en cache, votre application utilise uniquement les versions mises en cache des fichiers et n’essaie pas de télécharger de nouvelles versions.

Pour vous assurer que les utilisateurs obtiennent les dernières modifications apportées aux ressources statiques utilisées par votre application, utilisez une convention de nommage de mise en cache et mettez à jour votre code de travail de service.

Le busting du cache signifie que chaque fichier statique est nommé en fonction de sa version. Cela peut être réalisé de différentes façons, mais implique généralement l’utilisation d’un outil de génération qui lit le contenu d’un fichier et génère un ID unique basé sur le contenu. Cet ID peut être utilisé pour nommer le fichier statique mis en cache.

Ensuite, mettez à jour le code de votre service Worker pour mettre en cache les nouvelles ressources statiques pendant install:

// The name of the new cache your app uses.
const CACHE_NAME = "my-app-cache-v2";
// The list of static files your app needs to start.
const PRE_CACHED_RESOURCES = ["/", "styles-124656.css", "app-576391.js"];

// Listen to the `install` event.
self.addEventListener("install", event => {
  async function preCacheResources() {
    // Open the app's cache.
    const cache = await caches.open(CACHE_NAME);
    // Cache the new static resources.
    cache.addAll(PRE_CACHED_RESOURCES);
  }

  event.waitUntil(preCacheResources());
});

// Listen to the `activate` event to clear old caches.
self.addEventListener("activate", event => {
  async function deleteOldCaches() {
    // List all caches by their names.
    const names = await caches.keys();
    await Promise.all(names.map(name => {
      if (name !== CACHE_NAME) {
        // If a cache's name is the current name, delete it.
        return caches.delete(name);
      }
    }));
  }

  event.waitUntil(deleteOldCaches());
});

Comparez les CACHE_NAME valeurs et PRE_CACHED_RESOURCES entre l’extrait de code ci-dessus et celui des ressources de pré-mise en cache. Lorsque ce nouveau service Worker est installé, un nouveau cache est créé et les nouvelles ressources statiques sont téléchargées et mises en cache. Lorsque le service Worker est activé, l’ancien cache est supprimé. À ce stade, l’utilisateur disposera de la nouvelle version de l’application.

Apporter des modifications à votre service Worker peut parfois être complexe. Utilisez une bibliothèque telle que Workbox pour simplifier l’étape de génération de vos ressources statiques et votre code de travail de service.

Tester les connexions réseau dans votre PWA

Il est utile de savoir quand une connexion réseau est disponible, afin de synchroniser les données ou d’informer les utilisateurs que l’état du réseau a changé.

Utilisez les options suivantes pour tester la connectivité réseau :

La navigator.onLine propriété est une valeur booléenne qui vous permet de connaître l’état actuel du réseau. Si la valeur est true, l’utilisateur est en ligne ; sinon, l’utilisateur est hors connexion.

Pour plus d’informations , consultez navigator.onLine sur MDN.

Événements en ligne et hors connexion

Vous pouvez prendre des mesures lorsque votre connectivité réseau change. Vous pouvez écouter et prendre des mesures en réponse aux événements réseau. Les événements sont disponibles sur les windowéléments , documentet document.body , comme indiqué ci-dessous :

window.addEventListener("online",  function(){
    console.log("You are online!");
});
window.addEventListener("offline", function(){
    console.log("Network connection lost!");
});

Pour en savoir plus, consultez Navigator.onLine sur MDN.

Autres fonctionnalités

La responsabilité principale d’un worker de service est de rendre votre application plus rapide et plus fiable en cas de connexion réseau instable. Un worker de service utilise généralement l’événement et Cache l’API fetch pour ce faire, mais un worker de service peut utiliser d’autres API pour des scénarios spécialisés, tels que :

  • Synchronisation en arrière-plan des données.
  • Synchronisation périodique des données.
  • Téléchargements de fichiers en arrière-plan volumineux.
  • Gestion et notifications des messages Push.

Synchronisation en arrière-plan

Utilisez l’API de synchronisation en arrière-plan pour permettre aux utilisateurs de continuer à utiliser votre application et d’effectuer des actions même lorsque l’appareil de l’utilisateur est hors connexion.

Par exemple, une application de messagerie peut permettre à ses utilisateurs de composer et d’envoyer des messages à tout moment. Le serveur frontal de l’application peut essayer d’envoyer le message immédiatement, et si l’appareil est hors connexion, le service Worker peut intercepter la demande ayant échoué et utiliser l’API de synchronisation en arrière-plan pour différer la tâche jusqu’à ce qu’il soit connecté.

Pour plus d’informations, consultez Utiliser l’API de synchronisation en arrière-plan pour synchroniser les données avec le serveur.

Synchronisation en arrière-plan de période

L’API périodique de synchronisation en arrière-plan permet aux PWA de récupérer régulièrement du contenu nouveau, en arrière-plan, afin que les utilisateurs puissent accéder immédiatement au contenu lorsqu’ils ouvriront l’application ultérieurement.

À l’aide de l’API de synchronisation en arrière-plan périodique, les PWA n’ont pas besoin de télécharger de nouveaux contenus (tels que de nouveaux articles) pendant que l’utilisateur utilise l’application. Le téléchargement de contenu peut ralentir l’expérience, de sorte que l’application peut récupérer le contenu à un moment plus pratique.

Pour plus d’informations, consultez Utiliser l’API de synchronisation en arrière-plan périodique pour obtenir régulièrement du contenu actualisé.

Téléchargements de fichiers d’arrière-plan volumineux

L’API De récupération en arrière-plan permet aux PWA de déléguer complètement le téléchargement de grandes quantités de données au moteur du navigateur. De cette façon, le worker d’application et de service n’a pas besoin d’être en cours d’exécution pendant le téléchargement.

Cette API est utile pour les applications qui permettent aux utilisateurs de télécharger des fichiers volumineux (tels que de la musique, des films ou des podcasts) pour des cas d’usage hors connexion. Le téléchargement est délégué au moteur du navigateur, qui sait comment gérer une connexion intermittente ou même une perte complète de connectivité.

Pour plus d’informations, consultez Utiliser l’API d’extraction en arrière-plan pour extraire des fichiers volumineux lorsque l’application ou le service Worker n’est pas en cours d’exécution.

Messages Push

Des messages Push peuvent être envoyés à vos utilisateurs sans qu’ils n’utilisent l’application à ce moment-là. Un service Worker peut écouter les messages Push envoyés par votre serveur même si l’application n’est pas en cours d’exécution et afficher une notification dans le centre de notifications du système d’exploitation.

Pour en savoir plus, consultez Réengager les utilisateurs avec des messages Push.

Déboguer avec DevTools

À l’aide de Microsoft Edge DevTools, vous pouvez voir si votre service Worker a été inscrit correctement et voir dans quel état de cycle de vie il se trouve actuellement. En outre, vous pouvez déboguer le code JavaScript dans votre service Worker.

Pour en savoir plus, consultez Déboguer votre worker de service.

Voir aussi