`Service Worker` in JavaScript
Dit artikel legt het concept van Service Worker
in JavaScript uit.
We leggen stap voor stap uit, van de basis van Service Worker
tot praktische cachecontrole.
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
in JavaScript
Service Worker
is een JavaScript-functie die zich tussen de browser en het netwerk bevindt, waardoor verzoeken kunnen worden gecached en offline-ondersteuning mogelijk is. Het is een kerntechnologie van PWA's (Progressive Web Apps) en biedt een app-achtige ervaring aan webapplicaties.
Wat is een Service Worker
?
Service Worker
is een JavaScript-bestand dat draait in de achtergrondthread van de browser. Het draait op een aparte thread van de pagina, heeft geen toegang tot de UI, maar kan netwerkverzoeken onderscheppen, caching beheren en pushmeldingen verwerken.
De belangrijkste kenmerken van een Service Worker
zijn onder andere:.
- Het werkt alleen over HTTPS, behalve op localhost.
- Het gebruikt een Promise-gebaseerde asynchrone API.
- Het is gebeurtenisgestuurd, met gebeurtenissen zoals
install
,activate
,fetch
enpush
.
Service Worker
registreren
Laten we eerst de code schrijven om een Service Worker
in de browser te registreren.
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}
Uitleg
- Gebruik
navigator.serviceWorker.register()
om/sw.js
(hetService Worker
-bestand) te registreren. - Je kunt
then
gebruiken voor het afhandelen van succes encatch
voor het afhandelen van fouten tijdens de registratie. registration.scope
vertegenwoordigt het padbereik (scope) dat door deService Worker
wordt beïnvloed.- Standaard is de scope de map waarin het geregistreerde bestand zich bevindt (in dit geval
/sw.js
) en de submappen daarvan.
Service Worker
-scope
Als je de scope wilt beperken, kun je de scope
opgeven met het tweede argument van 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});
Uitleg
- Met deze instelling worden alleen pagina’s onder
/app/
beheerd door deService Worker
.
Het Service Worker
-bestand aanmaken
Maak vervolgens een bestand genaamd sw.js
aan en implementeer de basisevenementen.
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];
Deze code definieert een lijst met resources die in de cache moeten worden opgeslagen.
Rollen en mechanismen van elk evenement
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')
wordt geactiveerd wanneer deService Worker
voor de eerste keer wordt geregistreerd. In deze fase worden de benodigde bestanden vooraf in de cache geplaatst.
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});
- Tijdens het
activate
-evenement worden oude caches verwijderd om opslag te optimaliseren. Alleen de cache van de nieuwe versie blijft behouden.
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});
Alle HTTP-verzoeken worden onderschept: als er een gecachte versie bestaat, wordt deze teruggegeven; anders wordt deze van het netwerk opgehaald. Als je offline bent, wordt een alternatieve pagina (bijvoorbeeld offline.html
) teruggegeven.
De werking bevestigen
Laten we daadwerkelijk controleren hoe de Service Worker
werkt.
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});
- Hier controleren we de registratie van de
Service Worker
en het ophalen van middelen door op de testknop te klikken.
Voorbeelden van cachingstrategieën
De volgende zijn veelgebruikte cachingstrategieën:.
Cache First
Hier is een voorbeeldimplementatie voor de Cache First-strategie:.
1self.addEventListener('fetch', event => {
2 event.respondWith(
3 caches.match(event.request).then(response => {
4 return response || fetch(event.request);
5 })
6 );
7});
- Deze code implementeert een cache-first strategie, waarbij de opgevraagde bron uit de cache wordt teruggegeven als deze beschikbaar is; zoniet, wordt de bron van het netwerk gehaald.
Network First
Hier is een voorbeeldimplementatie voor de Network First-strategie:.
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});
- Deze code implementeert een network-first strategie, waarbij de opgevraagde bron eerst van het netwerk wordt gehaald en, als dat mislukt, uit de cache wordt opgehaald.
Cache alleen stijlen en JavaScript, benader API's in realtime
Hier is een voorbeeldimplementatie waarbij stijlen en JavaScript worden gecached, terwijl API's in realtime worden benaderd:.
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});
- Deze code maakt altijd real-time verbinding met API-aanvragen en past een cache-first strategie toe op statische bestanden zoals stylesheets en JavaScript.
Update-proces
Het updateproces van een Service Worker verloopt als volgt:.
- Een nieuwe
sw.js
wordt gedetecteerd. - Het
install
-evenement wordt geactiveerd. - Er wordt gewacht tot de vorige
Service Worker
geen taken meer heeft. - Het
activate
-evenement wordt geactiveerd. - Er wordt overgeschakeld naar de nieuwe Service Worker.
- Het
controllerchange
-event wordt getriggerd.
Update-detectie
Zodra een Service Worker
is geïnstalleerd, blijft de oude tot het volgende bezoek in gebruik. Om updates toe te passen, is het gebruikelijk om code te gebruiken die updates detecteert en de pagina vernieuwt.
1navigator.serviceWorker.addEventListener('controllerchange', () => {
2 window.location.reload();
3});
- Het
controllerchange
-event wordt getriggerd wanneer de controller van de Service Worker, dus de Service Worker die de huidige pagina bestuurt, verandert. - Pagina's die al openstaan, blijven de huidige Service Worker gebruiken en de nieuw geïnstalleerde Service Worker wordt niet direct actief op die pagina's. Daarom wordt een techniek gebruikt waarbij het
controllerchange
-event wordt gebruikt om te detecteren dat er een nieuwe controller actief is geworden; vervolgens wordt de pagina vernieuwd om de update onmiddellijk toe te passen.
Let op en best practices
Houd bij het gebruik van een Service Worker
rekening met het volgende:.
-
HTTPS vereist Vanwege beveiligingsbeperkingen werkt het niet via
http://
behalve oplocalhost
. -
Gehashte bestandsnamen De cchenaam kan de bestandsnaam, URL en versiesinformatie bevatten.
-
Communicatie met clients Gebruik
postMessage
om te communiceren tussen deService Worker
en de JavaScript van de pagina.
Samenvatting
Service Worker
is een essentiële technologie voor offline-ondersteuning en prestatieverbeteringen in webapps. Door het basisproces van installatie, activatie en verwerking van fetch te begrijpen en geschikte cachingstrategieën te implementeren, kun je webapplicaties van hogere kwaliteit bouwen.
Je kunt het bovenstaande artikel volgen met Visual Studio Code op ons YouTube-kanaal. Bekijk ook het YouTube-kanaal.