`Service Worker` en JavaScript
Cet article explique le concept de Service Worker
en JavaScript.
Nous expliquerons étape par étape les bases du Service Worker
jusqu’au contrôle pratique du cache.
YouTube Video
offline.html
1<!DOCTYPE html>
2<html lang="en">
3<head>
4 <meta charset="UTF-8">
5 <title>Offline</title>
6</head>
7<body>
8 <h1>You are offline</h1>
9 <p>This is the offline fallback page.</p>
10</body>
11</html>
style.css
1body {
2 font-family: sans-serif;
3 background-color: #f0f0f0;
4 padding: 20px;
5}
6h1 {
7 color: #333;
8}
javascript-service-worker.html
1<!DOCTYPE html>
2<html lang="en">
3<head>
4 <meta charset="UTF-8">
5 <title>JavaScript & HTML</title>
6 <style>
7 * {
8 box-sizing: border-box;
9 }
10
11 body {
12 margin: 0;
13 padding: 1em;
14 padding-bottom: 10em;
15 font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
16 background-color: #f7f9fc;
17 color: #333;
18 line-height: 1.6;
19 }
20
21 .container {
22 max-width: 800px;
23 margin: 0 auto;
24 padding: 1em;
25 background-color: #ffffff;
26 border: 1px solid #ccc;
27 border-radius: 10px;
28 box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
29 }
30
31 .container-flex {
32 display: flex;
33 flex-wrap: wrap;
34 gap: 2em;
35 max-width: 1000px;
36 margin: 0 auto;
37 padding: 1em;
38 background-color: #ffffff;
39 border: 1px solid #ccc;
40 border-radius: 10px;
41 box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
42 }
43
44 .left-column, .right-column {
45 flex: 1 1 200px;
46 min-width: 200px;
47 }
48
49 h1, h2 {
50 font-size: 1.2rem;
51 color: #007bff;
52 margin-top: 0.5em;
53 margin-bottom: 0.5em;
54 border-left: 5px solid #007bff;
55 padding-left: 0.6em;
56 background-color: #e9f2ff;
57 }
58
59 button {
60 display: block;
61 margin: 1em auto;
62 padding: 0.75em 1.5em;
63 font-size: 1rem;
64 background-color: #007bff;
65 color: white;
66 border: none;
67 border-radius: 6px;
68 cursor: pointer;
69 transition: background-color 0.3s ease;
70 }
71
72 button:hover {
73 background-color: #0056b3;
74 }
75
76 #output {
77 margin-top: 1em;
78 background-color: #1e1e1e;
79 color: #0f0;
80 padding: 1em;
81 border-radius: 8px;
82 min-height: 200px;
83 font-family: Consolas, monospace;
84 font-size: 0.95rem;
85 overflow-y: auto;
86 white-space: pre-wrap;
87 }
88
89 .highlight {
90 outline: 3px solid #ffc107; /* yellow border */
91 background-color: #fff8e1; /* soft yellow background */
92 transition: background-color 0.3s ease, outline 0.3s ease;
93 }
94
95 .active {
96 background-color: #28a745; /* green background */
97 color: #fff;
98 box-shadow: 0 0 10px rgba(40, 167, 69, 0.5);
99 transition: background-color 0.3s ease, box-shadow 0.3s ease;
100 }
101 </style>
102</head>
103<body>
104 <div class="container">
105 <h1>JavaScript Console</h1>
106 <button id="executeBtn">Execute</button>
107 <div id="output"></div>
108 </div>
109
110 <div class="container">
111 <h2>HTML Sample</h2>
112 <button id="fetchBtn">Fetch Test</button>
113 </div>
114
115 <script>
116 // Override console.log to display messages in the #output element
117 (function () {
118 // Override console.log
119 const originalLog = console.log;
120 console.log = function (...args) {
121 originalLog.apply(console, args);
122 const message = document.createElement('div');
123 message.textContent = args.map(String).join(' ');
124 output.appendChild(message);
125 };
126
127 // Override console.error
128 const originalError = console.error;
129 console.error = function (...args) {
130 originalError.apply(console, args);
131 const message = document.createElement('div');
132 message.textContent = args.map(String).join(' ');
133 message.style.color = 'red'; // Color error messages red
134 output.appendChild(message);
135 };
136 })();
137
138 document.getElementById('executeBtn').addEventListener('click', () => {
139 // Prevent multiple loads
140 if (document.getElementById('externalScript')) return;
141
142 const script = document.createElement('script');
143 script.src = 'javascript-service-worker.js';
144 script.id = 'externalScript';
145 //script.onload = () => console.log('javascript-service-worker.js loaded and executed.');
146 //script.onerror = () => console.log('Failed to load javascript-service-worker.js.');
147 document.body.appendChild(script);
148 });
149 </script>
150</body>
151</html>
Service Worker
en JavaScript
Service Worker
est une fonctionnalité JavaScript qui se situe entre le navigateur et le réseau, permettant la mise en cache des requêtes et la prise en charge hors ligne. C’est une technologie clé des PWA (Progressive Web Apps) et elle apporte une expérience similaire à une application native aux applications web.
Qu’est-ce qu’un Service Worker
?
Service Worker
est un fichier JavaScript qui s’exécute dans le thread d’arrière-plan du navigateur. Il s’exécute sur un thread séparé de la page, ne peut pas accéder à l’interface utilisateur, mais peut intercepter les requêtes réseau, gérer la mise en cache et traiter les notifications push.
Les principales fonctionnalités d’un Service Worker
incluent :.
- Il ne fonctionne que via HTTPS, sauf sur localhost.
- Il utilise une API asynchrone basée sur les Promises.
- Il est basé sur des événements, utilisant des événements comme
install
,activate
,fetch
, etpush
.
Enregistrer un Service Worker
Tout d’abord, écrivons le code pour enregistrer un Service Worker
dans le navigateur.
1if ('serviceWorker' in navigator) {
2 window.addEventListener('load', () => {
3 navigator.serviceWorker.register('/sw.js')
4 .then(registration => {
5 console.log(
6 'Service Worker registered with scope:',
7 registration.scope
8 );
9 })
10 .catch(error => {
11 console.error('Service Worker registration failed:', error);
12 });
13 });
14}
Explication
- Utilisez
navigator.serviceWorker.register()
pour enregistrer/sw.js
(le fichier duService Worker
). - Vous pouvez utiliser
then
pour la gestion du succès etcatch
pour la gestion des erreurs lors de l’enregistrement. registration.scope
représente l’étendue (scope) du chemin affectée par leService Worker
.- Par défaut, le scope correspond au répertoire où se trouve le fichier enregistré (dans ce cas,
/sw.js
) et à ses sous-répertoires.
Périmètre du Service Worker
Si vous souhaitez limiter le scope, vous pouvez le spécifier avec le deuxième argument de register
.
1navigator.serviceWorker.register('/sw.js', { scope: '/app/' })
2.then(registration => {
3 console.log(
4 'Service Worker registered with scope:',
5 registration.scope
6 );
7});
Explication
- Avec ce paramètre, seules les pages sous
/app/
seront contrôlées par leService Worker
.
Créer le fichier Service Worker
Ensuite, créez un fichier nommé sw.js
et implémentez les événements de base.
1// sw.js
2const CACHE_NAME = 'my-cache-v1';
3const urlsToCache = [
4 '/',
5 '/index.html',
6 '/styles.css',
7 '/script.js',
8 '/offline.html'
9];
Ce code définit une liste de ressources à mettre en cache.
Rôles et mécanismes de chaque événement
install
1// Install event (initial caching)
2self.addEventListener('install', event => {
3 console.log('[ServiceWorker] Install');
4 event.waitUntil(
5 caches.open(CACHE_NAME).then(cache => {
6 console.log('[ServiceWorker] Caching app shell');
7 return cache.addAll(urlsToCache);
8 })
9 );
10});
self.addEventListener('install')
est déclenché lors du premier enregistrement duService Worker
. À ce stade, les fichiers nécessaires sont pré-cacheés.
activate
1// Activation event (delete old caches)
2self.addEventListener('activate', event => {
3 console.log('[ServiceWorker] Activate');
4 event.waitUntil(
5 caches.keys().then(keyList => {
6 return Promise.all(keyList.map(key => {
7 if (key !== CACHE_NAME) {
8 console.log('[ServiceWorker] Removing old cache:', key);
9 return caches.delete(key);
10 }
11 }));
12 })
13 );
14 return self.clients.claim();
15});
- Lors de l’événement
activate
, les anciens caches sont supprimés pour optimiser le stockage. Seul le cache de la nouvelle version est conservé.
fetch
1// Fetch event (cache-first strategy)
2self.addEventListener('fetch', event => {
3 console.log('[ServiceWorker] Fetch', event.request.url);
4 event.respondWith(
5 caches.match(event.request).then(response => {
6 return response || fetch(event.request).catch(() => caches.match('/offline.html'));
7 })
8 );
9});
Toutes les requêtes HTTP sont interceptées—si une version en cache existe, elle est renvoyée ; sinon, elle est récupérée depuis le réseau. En mode hors ligne, une page alternative (par ex., offline.html
) est renvoyée.
Confirmation de l’opération
Voyons concrètement comment fonctionne le Service Worker
.
1document.getElementById('fetchBtn').addEventListener('click', () => {
2 fetch('/style.css')
3 .then(response => response.text())
4 .then(data => {
5 console.log('Fetched data:', data);
6 })
7 .catch(error => {
8 console.error('Fetch failed:', error);
9 });
10});
- Ici, nous vérifions l’enregistrement du
Service Worker
et le comportement de la récupération des ressources en cliquant sur le bouton de test.
Exemples de stratégies de mise en cache
Voici des stratégies de mise en cache courantes :.
Cache d’abord (Cache First)
Voici un exemple d’implémentation pour la stratégie Cache First :.
1self.addEventListener('fetch', event => {
2 event.respondWith(
3 caches.match(event.request).then(response => {
4 return response || fetch(event.request);
5 })
6 );
7});
- Ce code implémente une stratégie « cache-first », où la ressource demandée est renvoyée depuis le cache si elle est disponible ; sinon, elle est récupérée depuis le réseau.
Réseau d’abord (Network First)
Voici un exemple d’implémentation pour la stratégie Network First :.
1self.addEventListener('fetch', event => {
2 event.respondWith(
3 fetch(event.request)
4 .then(response => {
5 return caches.open(CACHE_NAME).then(cache => {
6 cache.put(event.request, response.clone());
7 return response;
8 });
9 })
10 .catch(() => caches.match(event.request))
11 );
12});
- Ce code implémente une stratégie « network-first », où la ressource demandée est d'abord récupérée depuis le réseau, et en cas d'échec, elle est obtenue à partir du cache.
Mettre en cache uniquement les styles et JavaScript, accéder aux APIs en temps réel
Voici un exemple d’implémentation où les fichiers de styles et JavaScript sont mis en cache, tandis que les APIs sont consultées en temps réel :.
1self.addEventListener('fetch', event => {
2 if (event.request.url.includes('/api/')) {
3 // Fetch API responses in real-time without caching
4 return;
5 }
6
7 // Use cache-first strategy for static files
8 event.respondWith(
9 caches.match(event.request).then(response => {
10 return response || fetch(event.request);
11 })
12 );
13});
- Ce code accède toujours aux requêtes API en temps réel et applique une stratégie « cache-first » aux fichiers statiques tels que les feuilles de style et les fichiers JavaScript.
Processus de mise à jour
Le processus de mise à jour d’un Service Worker est le suivant :.
- Un nouveau
sw.js
est détecté. - L’événement
install
est déclenché. - Le système attend que l’ancien
Service Worker
soit inactif. - L’événement
activate
est déclenché. - Il passe au nouveau Service Worker.
- L’événement
controllerchange
est déclenché.
Détection de mise à jour
Une fois un Service Worker
installé, l’ancien continue d’être utilisé jusqu’à la prochaine visite. Pour appliquer les mises à jour, il est courant d’utiliser un code qui détecte les mises à jour et recharge la page.
1navigator.serviceWorker.addEventListener('controllerchange', () => {
2 window.location.reload();
3});
- L’événement
controllerchange
est déclenché lorsque le contrôleur du Service Worker, c’est-à-dire le Service Worker contrôlant la page actuelle, change. - Les pages déjà ouvertes continuent d’utiliser le Service Worker actuel, et le nouveau Service Worker installé ne prend pas effet immédiatement sur ces pages. Par conséquent, une technique consiste à utiliser l’événement
controllerchange
pour détecter qu’un nouveau contrôleur est actif, puis à recharger la page pour appliquer immédiatement la mise à jour.
Précautions et bonnes pratiques
Lorsque vous utilisez un Service Worker
, gardez les points suivants à l’esprit :.
-
HTTPS requis En raison de restrictions de sécurité, il ne fonctionne pas via
http://
sauf surlocalhost
. -
Noms de fichiers hachés Le nom du cache peut inclure le nom du fichier, l’URL et des informations de version.
-
Communication avec les clients Utilisez
postMessage
pour communiquer entre leService Worker
et le JavaScript de la page.
Résumé
Service Worker
est une technologie essentielle pour la prise en charge hors ligne et l’amélioration des performances des applications web. En comprenant le déroulement de l’installation, de l’activation et de la gestion des requêtes, puis en mettant en œuvre des stratégies de mise en cache appropriées, vous pouvez développer des applications web de meilleure qualité.
Vous pouvez suivre l'article ci-dessus avec Visual Studio Code sur notre chaîne YouTube. Veuillez également consulter la chaîne YouTube.