Créer un script pour le service worker

Cet atelier de programmation fait partie du cours de développement des progressive web apps, développé par l'équipe de formation Google Developers. Exploitez tout le potentiel de ce cours en suivant les ateliers de programmation dans l'ordre.

Pour en savoir plus, consultez la Présentation des progressive web apps.

Introduction

Cet atelier vous explique comment créer un service worker simple et décrire le cycle de vie du service worker.

Points abordés

  • Créer un script de service worker de base, l'installer et effectuer un débogage simple

Bon à savoir

  • Code JavaScript et HTML de base
  • Concepts et syntaxe de base d'ES2015 Promis
  • Activer la console pour les développeurs

Ce dont vous avez besoin avant de commencer

Téléchargez ou clonez le dépôt pwa-training-labsà partir de GitHub, puis installez la version LTS de Node.js, si nécessaire.

Accédez au répertoire service-worker-lab/app/ et démarrez un serveur de développement local:

cd service-worker-lab/app
npm install
node server.js

Vous pouvez mettre fin au serveur à tout moment avec Ctrl-c.

Ouvrez votre navigateur et accédez à localhost:8081/.

Remarque : Annulez l'enregistrement des nœuds de calcul de service et videz le cache de tous les nœuds de calcul de l'hôte local afin qu'ils n'interfèrent pas avec l'atelier. Pour ce faire, cliquez sur Effacer les données du site dans la section Effacer les données de stockage de l'onglet Application dans les outils pour les développeurs Chrome.

Ouvrez le dossier service-worker-lab/app/ dans l'éditeur de texte de votre choix. Le dossier app/ correspond à l'endroit où vous allez créer l'atelier.

Ce dossier contient:

  • below/another.html, js/another.js, js/other.js et other.html sont des exemples de ressources que nous utilisons pour tester la portée du service worker
  • styles/ dossier contient les feuilles de style en cascade pour cet atelier
  • Le dossier test/ contient des fichiers pour tester votre progression
  • index.html est la page HTML principale de notre exemple de site/application.
  • service-worker.js est le fichier JavaScript utilisé pour créer notre service worker.
  • package.json et package-lock.json suivent les packages de nœuds utilisés dans ce projet.
  • server.js est un serveur Express simple que nous utilisons pour héberger notre application.

Ouvrez service-worker.js dans votre éditeur de texte. Notez que le fichier est vide. Nous n'avons pas encore ajouté de code à exécuter dans le service worker.

Ouvrez index.html dans votre éditeur de texte.

Dans les balises <script>, ajoutez le code suivant pour enregistrer le service worker:

if ('serviceWorker' in navigator) {
  window.addEventListener('load', () => {
    navigator.serviceWorker.register('service-worker.js')
    .then(registration => {
      console.log('Service Worker is registered', registration);
    })
    .catch(err => {
      console.error('Registration failed:', err);
    });
  });
}

Enregistrez le script et actualisez la page. La console doit renvoyer un message indiquant que le service worker a été enregistré. Dans Chrome, vous pouvez vérifier qu'un service worker est enregistré. Pour ce faire, ouvrez les Outils de développement (Ctrl+Maj+I sous Windows et Linux, ou ⌘+Alt+I sous Mac), cliquez sur l'onglet Application, puis sur l'option Service Workers. Le résultat qui s'affiche doit ressembler à ceci :

Facultatif: ouvrez le site dans un navigateur non compatible et vérifiez que la vérification de l'assistance fonctionne.

Explanation

Le code ci-dessus enregistre le fichier service-worker.js en tant que service worker. Il vérifie d'abord si le navigateur est compatible avec les service worker. Vous devez effectuer cette opération chaque fois que vous enregistrez un service worker, car certains navigateurs ne sont pas compatibles avec ce type de service. Le code enregistre ensuite le service worker à l'aide de la méthode register de l'API ServiceWorkerContainer, qui est disponible dans l'interface Navigator de la fenêtre.

navigator.serviceWorker.register(...) renvoie une promesse qui se résout avec un objet registration une fois le service worker enregistré. Si l'enregistrement échoue, la promesse sera refusée.

Les modifications de l'état du service worker déclenchent des événements dans celui-ci.

Ajouter des écouteurs d'événements

Ouvrez service-worker.js dans votre éditeur de texte.

Ajoutez les écouteurs d'événements suivants au service worker:

self.addEventListener('install', event => {
  console.log('Service worker installing...');
  // Add a call to skipWaiting here
});

self.addEventListener('activate', event => {
  console.log('Service worker activating...');
});

Enregistrez le fichier.

Annulez manuellement l'enregistrement du service worker, puis actualisez la page pour l'installer et l'activer. Le journal de la console doit indiquer que le nouveau service worker a été enregistré, installé et activé.

Remarque : Il est possible que le journal d'enregistrement ne s'affiche pas dans le bon ordre avec les autres journaux (installation et activation). Le service worker est exécuté simultanément avec la page. Nous ne pouvons donc pas garantir l'ordre des journaux (le journal des enregistrements provient de la page, tandis que les journaux d'installation et d'activation proviennent du service worker). Toutefois, les événements d'installation, d'activation et de service worker se produisent dans un ordre défini dans le service worker, et doivent toujours apparaître dans l'ordre prévu.

Explanation

Le service worker émet un événement install à la fin de l'enregistrement. Dans le code ci-dessus, un message est enregistré dans l'écouteur d'événements install, mais dans une application réelle, il s'agit d'un bon endroit pour mettre en cache les éléments statiques.

Lorsqu'un service worker est enregistré, son navigateur détecte s'il est nouveau (en raison de sa différence avec le service worker précédemment installé ou de l'absence de service worker enregistré pour ce site). Si le service worker est nouveau (dans le cas présent), le navigateur l'installe.

Le service worker émet un événement activate lorsqu'il prend le contrôle de la page. Le code ci-dessus consigne un message ici, mais cet événement est souvent utilisé pour mettre à jour les caches.

Un seul service worker peut être actif à la fois pour un champ d'application donné (voir la section "Découverte de la portée d'un service worker"), de sorte qu'il ne peut pas être activé tant que le service worker n'est plus utilisé. C'est pourquoi toutes les pages contrôlées par un service worker doivent être fermées avant qu'un nouveau service worker puisse prendre le relais. Comme nous avons annulé l'enregistrement du service worker existant, celui-ci a été activé immédiatement.

Remarque : Il suffit d'actualiser la page pour transférer le contrôle à un nouveau service worker, car la nouvelle page sera demandée avant le déchargement de la page actuelle, et l'ancien service worker ne sera pas utilisé.

Remarque:Vous pouvez également activer manuellement un nouveau service worker à l'aide de certains navigateurs (outil de développement) et de manière automatisée avec skipWaiting() (voir la section 3.4).

Mettre à jour le service worker

Ajoutez le commentaire suivant n'importe où dans le fichier service-worker.js :

// I'm a new service worker

Enregistrez le fichier et actualisez la page. Consultez les journaux dans la console. Vous remarquerez que le nouveau service worker s'installe, mais ne s'active pas. Dans Chrome, vous pouvez voir le service worker en attente dans l'onglet Application des outils de développement.

Fermez toutes les pages associées au service worker. Rouvrez ensuite l'localhost:8081/. Le journal de la console doit indiquer que le nouveau service worker a été activé.

Remarque:Si vous obtenez des résultats inattendus, assurez-vous que le cache HTTP est désactivé dans les outils de développement.

Explanation

Le navigateur détecte une différence d'octets entre le fichier de service (nouveau et existant) (en raison du commentaire ajouté). Le nouveau service worker est donc installé. Étant donné qu'un seul service worker peut être actif à la fois (pour un champ d'application donné), bien qu'il soit installé, il ne sera activé qu'une fois qu'il aura été utilisé. En fermant toutes les pages sous l'ancien service worker, nous pouvons l'activer.

Ignorer la phase d'attente

Un nouveau service worker peut être activé immédiatement, même s'il existe déjà un service worker, en ignorant la phase d'attente.

Dans service-worker.js, ajoutez un appel à skipWaiting dans l'écouteur d'événements install:

self.skipWaiting();

Enregistrez le fichier et actualisez la page. Notez que le nouveau service worker s'installe et s'active immédiatement, même si un service worker précédent était en contrôle.

Explanation

La méthode skipWaiting() permet à un service worker de s'activer dès la fin de l'installation. L'écouteur d'événements d'installation est un emplacement courant où passer l'appel skipWaiting(), mais il peut être appelé n'importe où pendant ou avant la phase d'attente. Consultez cette documentation pour savoir quand et comment utiliser skipWaiting(). Dans la suite de l'atelier, nous pouvons à présent tester le code d'un nouveau service sans annuler manuellement l'enregistrement.

Pour en savoir plus

Les service worker peuvent servir de proxy entre votre application Web et le réseau.

Ajoutons un écouteur de récupération pour intercepter les requêtes de notre domaine.

Ajoutez le code suivant à service-worker.js :

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

Enregistrez le script et actualisez la page pour installer et activer le service worker mis à jour.

Vérifiez dans la console qu'aucun événement de récupération n'a été enregistré. Actualisez la page et vérifiez à nouveau la console. Cette fois-ci, les événements de récupération doivent s'afficher pour la page et ses éléments (comme le CSS).

Cliquez sur les liens Autre page, Autre page et Retour.

Les événements de récupération de chacune des pages et de leurs éléments s'affichent dans la console. Tous les journaux sont-ils pertinents ?

Remarque : Si vous consultez une page et que le cache HTTP n'est pas désactivé, les éléments CSS et JavaScript peuvent être mis en cache localement. Si tel est le cas, vous ne verrez pas d'événements de récupération pour ces ressources.

Explanation

Le service worker reçoit un événement de récupération pour chaque requête HTTP envoyée par le navigateur dans les limites de son champ d'application. L'objet fetch event contient la requête. Détecter les événements de récupération dans le service worker est semblable à l'écoute des événements de clic dans le DOM. Dans notre code, lorsqu'un événement de récupération se produit, nous enregistrons l'URL demandée dans la console (en pratique, nous pouvons également créer et renvoyer notre propre réponse personnalisée avec des ressources arbitraires).

Pourquoi aucun événement de récupération n'a-t-il été enregistré lors de la première actualisation ? Par défaut, les événements de récupération d'une page ne passent pas par un service worker, sauf si la requête elle-même passe par un service worker. Cela garantit la cohérence sur votre site. De même, si une page se charge sans le service worker, ses sous-ressources le sont également.

Pour en savoir plus

Code de la solution

Pour obtenir une copie du code de travail, accédez au dossier 04-intercepting-network-requests/.

Les service worker sont associés à une portée. Le champ d'application du service worker détermine depuis quels chemins le service worker intercepte les requêtes.

Trouver la portée

Mettez à jour le code d'inscription dans index.html avec:

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

Actualisez le navigateur. Notez que la console indique le champ d'application du service worker (dans le cas présent, il s'agit de http://localhost:8081/).

Explanation

La promesse renvoyée par register() résout l'objet d'inscription, qui contient le champ d'application du service worker.

Le champ d'application par défaut correspond au chemin d'accès au fichier du service worker et s'étend à tous les répertoires inférieurs. Ainsi, un service worker dans le répertoire racine d'une application contrôle les requêtes provenant de tous les fichiers de cette application.

Déplacer le service worker

Déplacez service-worker.js dans le répertoire below/ et mettez à jour l'URL du service worker dans le code d'inscription de index.html.

Annulez l'enregistrement du service worker actuel dans le navigateur, puis actualisez la page.

La console indique que la portée du service worker est désormais de http://localhost:8081/below/. Dans Chrome, le champ d'application du service worker est également disponible dans l'onglet "Application" des outils de développement:

Sur la page principale, cliquez sur Autre page, Autre page et Retour. Quelles requêtes de récupération sont enregistrées ? Lesquelles ?

Explanation

Le champ d'application par défaut du service worker est le chemin d'accès au fichier du service worker. Comme le fichier du service worker est désormais dans below/, il s'agit de son champ d'application. La console ne consigne désormais que les événements de récupération pour another.html, another.css et another.js, car il s'agit des seules ressources disponibles dans le champ d'application du service worker.

Définir un champ d'application arbitraire

Replacez le service worker dans le répertoire racine du projet (app/) et mettez à jour l'URL du service worker dans le code d'inscription de index.html.

Utilisez la référence sur MDN pour définir le champ d'application du service worker sur le répertoire below/ à l'aide du paramètre facultatif dans register().

Annulez l'enregistrement du service worker et actualisez la page. Cliquez sur Autre page, Autre page et Retour.

Là encore, la console montre que le champ d'application du service worker est maintenant http://localhost:8081/below/, et les événements de récupération des journaux ne concernent que another.html, another.css et another.js.

Explanation

Vous pouvez définir une portée arbitraire en transmettant un paramètre supplémentaire lors de l'enregistrement, par exemple:

navigator.serviceWorker.register('/service-worker.js', {
  scope: '/kitten/'
});

Dans l'exemple ci-dessus, le champ d'application du service worker est défini sur /kitten/. Le service worker intercepte les requêtes provenant des pages /kitten/ et /kitten/lower/, mais pas de celles du type /kitten ou /.

Remarque : Vous ne pouvez pas définir un champ d'application arbitraire supérieur à la position réelle du service worker. Toutefois, si votre nœud de calcul de serveur est actif sur un client diffusé avec l'en-tête Service-Worker-Allowed, vous pouvez spécifier un champ d'application maximal pour ce service worker au-dessus de l'emplacement du service worker.

Pour en savoir plus

Code de la solution

Pour obtenir une copie du code de travail, accédez au dossier solution/.

Vous disposez à présent d'un service worker simple et opérationnel, et vous comprenez le cycle de vie de celui-ci.

Pour en savoir plus

Cycle de vie des nœuds de calcul de service

Pour voir tous les ateliers de programmation du cours de formation sur la PWA, consultez l'atelier de bienvenue.