Blog ENI : Toute la veille numérique !
🐠 -25€ dès 75€ 
+ 7 jours d'accès à la Bibliothèque Numérique ENI. Cliquez ici
Accès illimité 24h/24 à tous nos livres & vidéos ! 
Découvrez la Bibliothèque Numérique ENI. Cliquez ici
  1. Livres et vidéos
  2. Progressive Web App
  3. Service Worker
Extrait - Progressive Web App Utilisez les standards du web pour développer vos applications mobiles
Extraits du livre
Progressive Web App Utilisez les standards du web pour développer vos applications mobiles Revenir à la page d'achat du livre

Service Worker

Introduction

Un Service Worker est un fichier JavaScript ajouté à l’application. Il est totalement autonome vis-à-vis du code principal de l’application. Le but principal de ce fichier est d’intercepter l’ensemble des requêtes HTTP envoyées par l’application afin de faire éventuellement un traitement spécifique (mise en cache, gestion centralisée des erreurs, modification de la requête envoyée…). Nous ne sommes pas limités à un seul Service Worker. Nous pouvons en effet définir plusieurs Service Worker qui contrôleront chacun une partie de l’application.

Comparativement aux fichiers JavaScript que nous avons l’habitude de manipuler, un Service Worker a un cycle de vie un peu particulier. En effet, le code JavaScript que nous avons l’habitude d’écrire ne reste actif que durant le temps d’affichage de l’application web. Une fois cette application web fermée, les fichiers JavaScript associés ne sont plus fonctionnels. Pour un Service Worker, cela est totalement différent. En effet, une fois le Service Worker actif, celui-ci le reste même si l’application n’est plus affichée par l’utilisateur, même si l’onglet du navigateur est fermé, ou encore même si le navigateur lui-même n’est plus utilisé. Cette...

Enregistrement du Service Worker

Pour pouvoir enregistrer un Service Worker, nous allons faire appel à l’objet navigator.serviceWorker dans le code JavaScript de l’application. C’est la seule petite modification que nous allons devoir apporter à celle-ci. Toutes les autres seront réalisées directement dans le Service Worker.

Afin de respecter le côté progressif des Progressive Web Apps, nous allons tout d’abord vérifier que l’objet navigator.serviceWorker est défini pour le navigateur utilisé. Si c’est le cas, nous pourrons faire l’enregistrement via la méthode register.

Dans l’exemple ci-après, si l’API des Service Workers est supportée par le navigateur, nous enregistrons une nouvelle instance dont le code sera défini dans le fichier sw.js.

if (‘serviceWorker' in navigator) { 
 navigator.serviceWorker.register('/sw.js') 
  .then(reg => { 
    console.log('votre service worker a été enregistré!') ; 
  }).catch(error => { 
    console.error(error); 
  }); 
} 
else { 
   console.log('Ce navigateur ne supporte pas cette API'); 
} 

Ainsi, pour les navigateurs récents, l’expérience de l’utilisateur sera améliorée grâce à l’utilisation de cette API. Quant aux personnes utilisant des anciennes versions de navigateurs...

Communication avec le Service Worker

Avant de commencer la description des différents événements supportés par les Service Workers, il est important de faire un point sur la syntaxe générale que nous allons utiliser. En effet, quand nous sommes sur le thread principal (celui en charge de l’affichage de l’application), lorsque nous souhaitons écouter un événement (comme le click sur un bouton, le submit d’un formulaire ou encore le focus sur un champ de saisie...), nous faisons appel à la méthode addEventListener disponible sur les éléments HTML. Cependant, dans le cadre des Service Workers, nous n’avons pas accès aux éléments HTML. Alors comment pouvons-nous écouter des événements ? Pour cela, nous utilisons tout simplement le mot-clé self, qui représente l’instance du Service Worker et la même méthode addEventListener.

Dans l’exemple ci-après, nous écoutons un événement fictif event. Lorsque cet événement est émis, la fonction passée en paramètre est exécutée.

self.addEventListener('event', event => { 
 
}) ; 

Comme indiqué précédemment, le Service Worker n’a pas accès aux fonctionnalités du thread principal de l’application....

Événements

1. Événement fetch

L’événement principal que nous pouvons écouter est l’événement fetch (à ne pas confondre avec l’API présentée précédemment). Il faut voir le Service Worker comme un proxy entre le navigateur et le serveur. Toutes les requêtes vont passer par ce proxy : que ce soient les requêtes pour récupérer des données, les requêtes pour récupérer des ressources statiques et même la toute première requête qui est en charge de télécharger le fichier index.html.

Lorsqu’une requête est envoyée par le navigateur, un événement fetch est émis, qui nous permet d’interagir avec cette requête et sa réponse associée dans le Service Worker. Nous pouvons par exemple modifier la requête ou la réponse, faire une gestion centralisée des erreurs, mettre en cache certains résultats... Pour cela, nous utilisons l’API Fetch.

Afin d’illustrer cet événement, nous allons implémenter trois fonctionnalités. Pour commencer, nous créons une toute nouvelle instance de l’objet Response. Ceci a pour effet d’afficher dans le navigateur la chaîne de caractères passée en paramètre, car la première requête que le Service Worker reçoit est celle liée au fichier index.html. Donc l’ensemble de l’application est remplacée par cette chaîne de caractères.

self.addEventListener('fetch', event => { 
   event.respondWith(new Response('PWA!!!')); 
}); 

Le constructeur de l’objet Response peut accepter un second paramètre qui permet de définir également le statut HTTP ainsi que les en-têtes de la réponse. Voici la structure de ce deuxième paramètre :

interface Response { 
  status, 
  statusText, 
  headers 
} 

L’exemple suivant permet de retourner une image prédéfinie à chaque fois qu’une requête est envoyée pour récupérer un fichier dont l’extension correspond à celle...

Utilisation de scripts externes

Au fur et à mesure que nous présenterons des API dans ce livre, le fichier JavaScript de votre Service Worker risque de devenir assez conséquent et donc illisible.

Pour respecter la même démarche que nous avons pour les applications JavaScript, nous pouvons structurer un Service Worker en de multiples fichiers et les charger dans un fichier JavaScript principal grâce à la méthode importScripts.

Ces fichiers seront téléchargés de manière unitaire par le navigateur lorsque l’un d’entre eux a été modifié.

Nous pouvons par exemple créer un fichier par événement du cycle de vie d’un Service Worker afin d’en améliorer la structure et la lisibilité.

 
importScripts("./install.js"); 
importScripts("./activate.js"); 
importScripts("./fetch.js");  

Un fichier est en charge d’un seul événement. Ainsi, lorsque nous devons réaliser une modification, il est très simple de savoir quel fichier doit être modifié. 

// install.js 
self.addEventListener("install", () => { 
    console.log("install event"); 
}); 

La méthode importScripts peut également être utilisée pour charger un module utilitaire...

Cache du Service Worker

Avant le cache côté navigateur (voir le chapitre Cache), un cache pouvait être implémenté via l’ajout d’en-tête dans les requêtes envoyées par le serveur au navigateur.

Depuis Chrome 68, le navigateur n’utilise plus son cache lors de la récupération du Service Worker, afin de s’assurer de récupérer toujours la dernière version de ce fichier.

Mais comme tous les navigateurs ne supportent pas cet algorithme, vous devez désactiver le cache du navigateur pour ce fichier envoyant l’en-tête HTTP Cache-Control: max-age: 0.

Pour rappel, pour les requêtes dont l’URL contient une fingerprint générée par l’outil de build, il faut mettre l’en-tête Cache-Control: max-age=31536000. En effet, lors d’une nouvelle mise en production de l’application, les fingerprints de chaque fichier de l’application seront différents car leurs générations sont basées sur le contenu de ces fichiers.

Pour les scripts importés via la méthode importScripts, le navigateur retourne par défaut une version en cache.

Ces stratégies peuvent être modifiées via le paramètre updateViaCache de la méthode register. Ce paramètre accepte trois valeurs, qui influent sur la stratégie de gestion...

Débogage d’un Service Worker

Un Service Worker est un fichier JavaScript comme les autres. Il est donc possible de le déboguer via un IDE (Integrated Development Environment) ou via la console d’un navigateur, par exemple de mettre des points d’arrêt, de vérifier l’état des variables, ou encore de modifier directement l’implémentation, afin de comprendre pourquoi il n’a pas le comportement souhaité.

Dans l’exemple suivant, pour le site https://devfest.gdglille.org/, nous pouvons ajouter par exemple un point d’arrêt pour intercepter toutes les requêtes qui sont envoyées (via l’événement fetch), et visualiser à gauche l’état des différentes variables.

images/06El01.png

Compatibilité navigateurs

Le tableau suivant récapitule le support des Service Workers par les principaux navigateurs du marché.

images/06El02.png

Pour rappel, comme nous chargeons le Service Worker en réalisant une feature detection, notre application fonctionnera normalement pour l’ensemble de ces navigateurs. Seuls les utilisateurs des navigateurs supportant les Service Workers auront une meilleure expérience avec notre application, grâce notamment aux fonctionnalités que nous présenterons dans les prochains chapitres.

Application fil rouge

Comme nous n’avons pas encore abordé les différentes API que nous pouvons utiliser dans un Service Worker, nous allons mettre en place une version très simple qui ne fera qu’afficher dans la console de notre navigateur toutes les requêtes sortantes.

Pour cela, dans la balise head de l’application, nous ajoutons le code suivant permettant d’enregistrer le Service Worker que nous allons implémenter dans un fichier sw.js.

<script> 
  if ('serviceWorker' in navigator) { 
     navigator.serviceWorker.register('/sw.js') 
      .then(reg => { 
        console.log('votre service worker a été enregistré!') 
      }).catch(error => { 
        console.error(error); 
      }); 
    } 
    else { 
      console.log('Ce navigateur ne supporte pas cette API'); 
} 
  </script> 

L’implémentation de notre Service Worker se limite à intercepter toutes les requêtes HTTP envoyées par notre navigateur, afin d’en afficher l’URL dans la console du navigateur. Pour cela, nous écoutons l’événement fetch et nous récupérons l’URL de la requête en manipulant l’objet event passé en paramètre. 

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

À présent, dans la console du navigateur, nous devrions voir les URL de toutes les requêtes sortantes.

https://cdn.jsdelivr.net/npm/pwacompat@2.0.9/pwacompat.min.js 
https://cdn.jsdelivr.net/npm/bulma@0.8.0/css/bulma.css 
http://localhost:5000/script.js 
https://api.github.com/users/EmmanuelDemey/repos 
http://localhost:5000/manifest.webmanifest 
http://localhost:5000/images/icons/icon-144x144.png 
https://bulma.io/images/placeholders/1280x960.png 
https://bulma.io/images/placeholders/96x96.png  

L’implémentation est très basique...