Comprendre et utiliser les travailleurs de service dans JavaScript pour les capacités hors ligne

Les utilisateurs du Web modernes attendent des expériences rapides et fiables même lorsque les conditions du réseau sont mauvaises ou inexistantes. Les travailleurs du Service sont la pierre angulaire de cette attente, permettant aux applications Web progressives (AFP) de travailler hors ligne, de se charger rapidement sur des connexions flaky et de fournir des notifications de poussée. Contrairement aux techniques traditionnelles de cache, un travailleur du Service agit comme un mandataire programmable assis entre le navigateur et le réseau. Il intercepte toutes les demandes sortantes de la page et peut répondre avec du contenu mis en cache, récupérer des données fraîches du réseau ou appliquer une stratégie hybride.

Les travailleurs de services ne sont pas une nouvelle technologie, ils sont pris en charge dans les principaux navigateurs depuis plusieurs années, mais ils restent sous-utilisés par de nombreuses équipes de développement. Lorsqu'ils sont construits correctement, ils transforment une application web statique en une expérience résiliente et similaire à une application. Nous allons passer par le cycle de vie complet, discuter des stratégies de cache qui conviennent à différents types de contenu, et couvrir les API compagnes comme Background Sync et Push Notifications.

Qu'est-ce qu'un travailleur de service?

Un Service Worker est un fichier JavaScript qui fonctionne dans un champ global séparé de votre page Web, sur son propre fil. Il n'a pas d'accès DOM et communique avec votre page uniquement par . Sa tâche principale est d'agir en tant qu'intermédiaire pour les demandes de réseau: le navigateur peut envoyer toute demande de votre page Web au Service Worker, qui décide ensuite comment répondre — du réseau, d'un cache, ou en construisant une réponse personnalisée.

Parce qu'il fonctionne en arrière-plan, un Service Worker peut continuer à fonctionner même après que l'utilisateur ferme la page qui l'a enregistré. Cela le rend idéal pour des tâches comme:

  • Support hors ligne – servir des pages mises en cache lorsque le réseau n'est pas disponible.
  • Synchronisation des données de fond[ – faire la queue des actions hors ligne et les envoyer lorsque la connectivité revient.
  • Notifications de type Push – recevoir des messages d'un serveur et les afficher à l'utilisateur même lorsque l'application n'est pas ouverte.
  • Optimisation du rendement – Pré-encachage des actifs critiques afin que l'application charge instantanément lors des visites répétées.

Les travailleurs-ses de services se fient à une exigence de sécurité stricte : ils ne travaillent que sous HTTPS (ou sur pour le développement), ce qui protège les utilisateurs des attaques de l'homme dans le milieu qui pourraient modifier le script.

Le cycle de vie des travailleurs de service

Un travailleur de service progresse dans un cycle de vie bien défini : enregistrement, installation, activation, puis manipulation par le ralenti/fetch. Comprendre ce cycle de vie est essentiel car il détermine quand vos ressources mises en cache deviennent disponibles et quand les anciens caches sont nettoyés.

1. Enregistrement

Avant que n'importe quelle logique de Service Worker puisse fonctionner, votre page doit enregistrer le script. Ceci est fait à partir de votre fichier JavaScript principal (habituellement le point d'entrée de page). L'enregistrement informe le navigateur de l'emplacement et de la portée du Service Worker. La portée définit quelles URLs le Service Worker peut contrôler; par défaut, il est le répertoire du script.

if ('serviceWorker' in navigator) {
 navigator.serviceWorker.register('/service-worker.js')
 .then(function(registration) {
 console.log('Service Worker registered with scope:', registration.scope);
 })
 .catch(function(error) {
 console.log('Registration failed:', error);
 });
}

Si le script existe déjà, le navigateur compare le contenu de l'octet pour l'octet avec la version précédemment installée. Si elle a changé, une nouvelle version est installée en arrière-plan tandis que l'ancienne version continue de contrôler les pages.

2. Installation

L'événement est le premier événement que votre Service Worker reçoit. C'est le moment idéal pour pré-cacher les fichiers essentiels dont votre application a besoin pour fonctionner hors ligne : le shell de l'application, le CSS central, JavaScript et toute page de repli.

const CACHE_NAME = 'my-cache-v1';
const urlsToCache = [
 '/',
 '/styles/main.css',
 '/scripts/main.js',
 '/offline.html'
];

self.addEventListener('install', function(event) {
 event.waitUntil(
 caches.open(CACHE_NAME)
 .then(function(cache) {
 console.log('Opened cache');
 return cache.addAll(urlsToCache);
 })
 );
});

Si l'un des fichiers ne parvient pas à être téléchargé pendant , l'étape d'installation échoue et le Service Worker n'est pas considéré comme installé. Il s'agit d'une fonction de sécurité pour vous assurer que vous n'avez jamais une application partiellement mise en cache.

3. Activation

Après l'installation, le navigateur attend que toutes les pages chargées avec l'ancien Service Worker soient fermées. Ensuite, il allume l'événement . Cet événement est généralement utilisé pour nettoyer les vieux caches et prendre le contrôle de toutes les pages qui ont été ouvertes avant l'activation.

self.addEventListener('activate', function(event) {
 const cacheWhitelist = [CACHE_NAME];
 event.waitUntil(
 caches.keys().then(function(cacheNames) {
 return Promise.all(
 cacheNames.map(function(cacheName) {
 if (cacheWhitelist.indexOf(cacheName) === -1) {
 return caches.delete(cacheName);
 }
 })
 );
 })
 );
});

Appeler à l'intérieur du gestionnaire prend immédiatement le contrôle de toutes les pages ouvertes sans nécessiter de recharge. Ceci est souvent utilisé pendant le développement pour une itération plus rapide.

4. Récupérer des événements

Une fois activé, le Service Worker écoute les événements . Chaque fois que le navigateur lance une demande depuis une page contrôlée, le Service Worker peut l'intercepter. Dans le gestionnaire d'événements de récupération, vous implémentez votre stratégie de cache de choix.

self.addEventListener('fetch', function(event) {
 event.respondWith(
 caches.match(event.request)
 .then(function(response) {
 // Cache hit - return response
 if (response) {
 return response;
 }
 return fetch(event.request);
 }
 )
 );
});

Stratégies de mise en cache pour différents scénarios

Le choix de la bonne stratégie de mise en cache détermine l'équilibre entre fraîcheur et vitesse. Il n'y a pas de solution unique; différentes ressources nécessitent des approches différentes.

Cache‐First (Cache puis Réseau)

Le Service Worker vérifie d'abord le cache; s'il est trouvé, il retourne immédiatement. Sinon, il récupère le réseau et cache la réponse. Cette stratégie produit des charges de répétition rapide.

self.addEventListener('fetch', function(event) {
 event.respondWith(
 caches.match(event.request)
 .then(function(response) {
 return response || fetch(event.request).then(function(networkResponse) {
 return caches.open(CACHE_NAME).then(function(cache) {
 cache.put(event.request, networkResponse.clone());
 return networkResponse;
 });
 });
 })
 );
});

Réseau‐First (Réseau avec Cache Fallback)

Idéal pour le contenu dynamique : les paramètres API, les flux d'actualités. Essayez le réseau d'abord; si elle échoue ou est très lente, revenez à la version cache. Cela garantit aux utilisateurs de toujours voir les dernières données à moins qu'elles ne soient hors ligne.

self.addEventListener('fetch', function(event) {
 event.respondWith(
 fetch(event.request)
 .then(function(networkResponse) {
 return caches.open(CACHE_NAME).then(function(cache) {
 cache.put(event.request, networkResponse.clone());
 return networkResponse;
 });
 })
 .catch(function() {
 return caches.match(event.request);
 })
 );
});

Stale‐Whil‐Revalidate

Le meilleur pour les ressources qui n'ont pas à être immédiatement fraîches : profil avatars, flux RSS. Répondez immédiatement avec la version cache, mais aussi récupérer une mise à jour du réseau en arrière-plan. La prochaine fois que la ressource est demandée, la nouvelle entrée cache est servie.

self.addEventListener('fetch', function(event) {
 event.respondWith(
 caches.open(CACHE_NAME).then(function(cache) {
 return cache.match(event.request).then(function(cachedResponse) {
 var fetchPromise = fetch(event.request).then(function(networkResponse) {
 cache.put(event.request, networkResponse.clone());
 return networkResponse;
 });
 return cachedResponse || fetchPromise;
 });
 })
 );
});

Réseau‐seulement et cache‐seulement

Pour les données sensibles telles que les données bancaires de l'utilisateur, utilisez Network‐Only (sans cache).Pour le contenu statique purement hors ligne (p. ex., un manuel), Cache‐Only peut être suffisant.

Fonctions avancées du travailleur de service

Au-delà de la mise en cache, les travailleurs de service débloquent de puissantes capacités de fond.

Synchronisation de l'arrière-plan

Lorsqu'un utilisateur soumet un formulaire ou envoie un message hors ligne, vous ne voulez pas perdre cette action. Le vous permet d'enregistrer un événement de synchronisation que le navigateur va déclencher une fois la connectivité de retour.

// In page script
navigator.serviceWorker.ready.then(function(registration) {
 return registration.sync.register('sync-messages');
});

// In Service Worker
self.addEventListener('sync', function(event) {
 if (event.tag === 'sync-messages') {
 event.waitUntil(sendPendingMessages());
 }
});

Contexte Sync est idéal pour les applications de chat, les présentations de formulaire ou toute file d'attente de données qui nécessite éventuellement une cohérence.

Notifications de poussée

Les messages poussoirs arrivent d'un serveur et réveillent le Service Worker, qui affiche ensuite une notification. Cela fonctionne même lorsque votre application web est fermée, donnant aux PWAs une sensation native.

self.addEventListener('push', function(event) {
 const options = {
 body: 'This notification was triggered by a push message.',
 icon: '/icons/icon-192x192.png',
 badge: '/icons/badge.png',
 data: {
 url: '/new-content'
 }
 };
 event.waitUntil(
 self.registration.showNotification('New Update', options)
 );
});

En cliquant sur la notification, vous pouvez ouvrir une URL spécifique en utilisant l'événement .

Sécurité et portée

Les travailleurs de service peuvent intercepter et modifier les requêtes, de sorte qu'elles doivent être servies sur HTTPS pour empêcher l'injection de scripts malveillants. Pendant le développement local, les navigateurs permettent aux travailleurs de service de comme exception.

Le scope d'un Service Worker détermine quelles pages il contrôle. Par défaut, la portée est le répertoire où se trouve le script Service Worker. Pour élargir la portée (par exemple, contrôler toutes les pages sous ), vous pouvez définir l'option pendant l'enregistrement. Vous devez également inclure un en-tête HTTP si vous voulez étendre la portée au-delà du répertoire script.

Travailleurs des services de débogage

Navigateur DevTools sont essentiels pour inspecter le comportement des travailleurs de service. Dans Chrome, ouvrir ou aller à l'application → travailleurs de service. Vous pouvez voir l'état de chaque travailleur, démarrer / arrêter manuellement, et les enregistrements clairs. Firefox offre des outils similaires sous environ:debugging.

Conseils de débogage courants:

  • Mise à jour sur recharge – dans Chrome DevTools, vérifiez -Mise à jour sur recharger - afin qu'un nouveau Service Worker soit installé chaque fois que la page recharge (utile pendant le développement).
  • Bypass pour réseau – désactive temporairement le Service Worker pour voir comment votre application se comporte sans elle.
  • Stockage de cache – Inspectez les ressources mises en cache dans le volet de stockage de cache pour vérifier votre stratégie de cache.
  • Loging – utilisez à l'intérieur du Service Worker; les messages apparaissent dans la console de fond dans DevTools.

Incidences sur les résultats

Les travailleurs de service peuvent améliorer considérablement les performances perçues, mais ils introduisent également la complexité. Pré-encachage de trop de ressources pendant l'installation peut retarder l'étape `install` et causer l'échec de l'installation si des temps s'écoulent. Une bonne pratique est de mettre en cache seulement le shell minimal dans et de mettre en cache paresseusement des ressources supplémentaires lors des événements de récupération.

La taille du cache est limitée par origine (habituellement quelques centaines de mégaoctets sur les navigateurs mobiles). La mise en cache répétée de grands fichiers multimédias peut remplir ce quota. Utilisez l'API de stockage () pour surveiller l'utilisation.

Autre piège de performance : si votre Service Worker effectue un calcul de poids lourd à l'intérieur du gestionnaire , il peut bloquer ou retarder les réponses réseau.

Pièges courants et comment les éviter

  • Servir du contenu intemporel – toujours versionner vos caches (p. ex. , ) et supprimer les anciens caches pendant l'événement . N'utilisez jamais un nom de cache non consolidé.
  • Page de retour non-calquée – si votre page hors ligne n'est pas pré-calquée, les utilisateurs verront une page d'erreur du navigateur. Cache un simple retour hors ligne pendant l'installation.
  • Demandes de POST en panne – Les travailleurs-ses de service peuvent intercepter les demandes de POST, mais vous devez être prudent avec les encastrer. Le ne supporte pas POST par défaut; les manipuler séparément ou simplement les passer.
  • Missing – si vous n'appelez pas , le navigateur va essayer de récupérer la ressource seul, contournant potentiellement votre logique de cache.

Exemple réel-monde : un AFP simple hors ligne-ready

Let , tout assemble. We , tout crée un petit PWA qui cache son shell et fournit un retour hors ligne. Le fichier complet Service Worker ():

const CACHE_NAME = 'pwa-shell-v2';
const SHELL_URLS = [
 '/',
 '/index.html',
 '/styles.css',
 '/app.js',
 '/offline.html'
];

// Install: pre‑cache shell
self.addEventListener('install', function(event) {
 event.waitUntil(
 caches.open(CACHE_NAME)
 .then(function(cache) {
 return cache.addAll(SHELL_URLS);
 })
 );
});

// Activate: clean old caches
self.addEventListener('activate', function(event) {
 const cacheWhitelist = [CACHE_NAME];
 event.waitUntil(
 caches.keys().then(function(cacheNames) {
 return Promise.all(
 cacheNames.map(function(name) {
 if (!cacheWhitelist.includes(name)) {
 return caches.delete(name);
 }
 })
 );
 }).then(function() {
 return self.clients.claim();
 })
 );
});

// Fetch: network‑first for pages, cache‑first for static assets
self.addEventListener('fetch', function(event) {
 if (event.request.mode === 'navigate') {
 // Pages: try network, fall back to cache
 event.respondWith(
 fetch(event.request).then(function(networkResponse) {
 // Update cache with fresh page
 const responseClone = networkResponse.clone();
 caches.open(CACHE_NAME).then(function(cache) {
 cache.put(event.request, responseClone);
 });
 return networkResponse;
 }).catch(function() {
 return caches.match(event.request).then(function(cached) {
 // If page not cached, show offline fallback
 return cached || caches.match('/offline.html');
 });
 })
 );
 } else {
 // Static assets: cache‑first
 event.respondWith(
 caches.match(event.request).then(function(cached) {
 return cached || fetch(event.request).then(function(networkResponse) {
 caches.open(CACHE_NAME).then(function(cache) {
 cache.put(event.request, networkResponse.clone());
 });
 return networkResponse;
 });
 })
 );
 }
});

Cet exemple utilise une première approche réseau pour la navigation (pages fraîches en ligne, repli hors ligne quand non) et cache-première pour tous les autres actifs. Le code gère la mise en cache en version complète.

Conclusion

Les travailleurs de service sont l'épine dorsale des applications Web modernes hors ligne. Ils fournissent une couche réseau programmable qui va bien au-delà de la simple mise en cache : synchronisation des arrière-plans, notifications de poussée et contrôle total de la façon dont les ressources sont servies.

Pour plonger plus profondément dans les API, consultez la documentation officielle sur MDN et le guide [Google Web Fundals.Pour les modèles de cache avancés, le Offline Cookbook de Jake Archibald propose des recettes détaillées. Commencez par une simple inscription, testez soigneusement dans DevTools, et itérer. Vos utilisateurs vous en remercieront.