`Service Worker` in JavaScript
Dieser Artikel erklärt das Konzept des Service Worker
in JavaScript.
Wir erklären Schritt für Schritt von den Grundlagen des Service Worker
bis hin zur praktischen Cache-Kontrolle.
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
ist eine JavaScript-Funktion, die zwischen dem Browser und dem Netzwerk steht und das Zwischenspeichern von Anfragen sowie Offline-Unterstützung ermöglicht. Es ist eine Kerntechnologie von PWAs (Progressive Web Apps) und bietet Webanwendungen ein benutzererlebnis wie native Apps.
Was ist ein Service Worker
?
Service Worker
ist eine JavaScript-Datei, die im Hintergrund-Thread des Browsers ausgeführt wird. Es läuft in einem separaten Thread von der Seite, hat keinen Zugriff auf das UI, kann aber Netzwerk-Anfragen abfangen, Caches verwalten und Push-Benachrichtigungen handhaben.
Zu den wichtigsten Funktionen eines Service Worker
gehören:.
- Es funktioniert nur über HTTPS, außer auf localhost.
- Es verwendet eine auf Promises basierende asynchrone API.
- Es ist ereignisgesteuert und verwendet Ereignisse wie
install
,activate
,fetch
undpush
.
Registrierung eines Service Worker
Zuerst schreiben wir den Code, um einen Service Worker
im Browser zu registrieren.
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}
Erklärung
- Verwenden Sie
navigator.serviceWorker.register()
, um/sw.js
(dieService Worker
-Datei) zu registrieren. - Sie können während der Registrierung
then
für die Erfolgsbehandlung undcatch
für die Fehlerbehandlung nutzen. registration.scope
repräsentiert den Pfadbereich (Scope), der vomService Worker
beeinflusst wird.- Standardmäßig ist der Scope das Verzeichnis, in dem sich die registrierte Datei (in diesem Fall
/sw.js
) befindet, sowie deren Unterverzeichnisse.
Service Worker
-Gültigkeitsbereich
Wenn Sie den Gültigkeitsbereich einschränken möchten, können Sie das scope
mit dem zweiten Argument von register
angeben.
1navigator.serviceWorker.register('/sw.js', { scope: '/app/' })
2.then(registration => {
3 console.log(
4 'Service Worker registered with scope:',
5 registration.scope
6 );
7});
Erklärung
- Mit dieser Einstellung werden nur Seiten unter
/app/
vomService Worker
kontrolliert.
Erstellen der Service Worker
-Datei
Als Nächstes erstellen Sie eine Datei namens sw.js
und implementieren die grundlegenden Ereignisse.
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];
Dieser Code definiert eine Liste von Ressourcen, die zwischengespeichert werden sollen.
Rollen und Mechanismen der einzelnen Ereignisse
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')
wird ausgelöst, wenn derService Worker
zum ersten Mal registriert wird. In dieser Phase werden die notwendigen Dateien vorab zwischengespeichert.
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});
- Im
activate
-Ereignis werden alte Caches gelöscht, um den Speicher zu optimieren. Nur der Cache der neuen Version wird beibehalten.
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-Anfragen werden abgefangen – wenn eine zwischengespeicherte Version existiert, wird diese zurückgegeben, andernfalls wird sie aus dem Netzwerk geladen. Im Offline-Modus wird eine alternative Seite (z. B. offline.html
) zurückgegeben.
Bestätigung des Vorgangs
Lassen Sie uns tatsächlich überprüfen, wie der Service Worker
funktioniert.
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 überprüfen wir die Registrierung des
Service Worker
und das Verhalten beim Abrufen von Ressourcen, indem wir auf den Testknopf klicken.
Beispiele für Caching-Strategien
Die folgenden sind gängige Caching-Strategien:.
Cache First
Hier ist eine Beispielimplementierung für die 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});
- Dieser Code implementiert eine Cache-First-Strategie, bei der die angeforderte Ressource aus dem Cache zurückgegeben wird, sofern verfügbar; andernfalls wird sie aus dem Netzwerk abgerufen.
Network First
Hier ist eine Beispielimplementierung für die 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});
- Dieser Code implementiert eine Network-First-Strategie, bei der die angeforderte Ressource zuerst aus dem Netzwerk abgerufen wird. Falls das fehlschlägt, wird sie aus dem Cache geladen.
Nur Styles und JavaScript werden zwischengespeichert, APIs werden in Echtzeit abgefragt
Hier ist eine Beispielimplementierung, bei der Styles und JavaScript zwischengespeichert werden, während die APIs in Echtzeit abgefragt werden:.
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});
- Dieser Code greift bei API-Anfragen immer in Echtzeit zu und verwendet eine Cache-First-Strategie für statische Dateien wie Stylesheets und JavaScript.
Update-Ablauf
Der Update-Ablauf eines Service Workers ist wie folgt:.
- Eine neue
sw.js
wird erkannt. - Das
install
-Ereignis wird ausgelöst. - Es wird gewartet, bis der vorherige
Service Worker
inaktiv ist. - Das
activate
-Ereignis wird ausgelöst. - Es wird auf den neuen Service Worker umgeschaltet.
- Das
controllerchange
-Ereignis wird ausgelöst.
Update-Erkennung
Sobald ein Service Worker
installiert ist, wird der alte weiterhin verwendet bis zum nächsten Seitenbesuch. Um Updates anzuwenden, verwendet man üblicherweise Code, der Updates erkennt und die Seite neu lädt.
1navigator.serviceWorker.addEventListener('controllerchange', () => {
2 window.location.reload();
3});
- Das
controllerchange
-Ereignis wird ausgelöst, wenn sich der Controller des Service Workers, also der Service Worker, der die aktuelle Seite steuert, ändert. - Bereits geöffnete Seiten verwenden weiterhin den aktuellen Service Worker, und der neu installierte Service Worker wird auf diesen Seiten nicht sofort wirksam. Daher wird eine Technik verwendet, bei der das
controllerchange
-Ereignis genutzt wird, um zu erkennen, dass ein neuer Controller aktiv geworden ist, und dann die Seite neu geladen wird, um das Update sofort anzuwenden.
Vorsichtsmaßnahmen und Best Practices
Beim Einsatz von Service Worker
sollten folgende Punkte beachtet werden:.
-
HTTPS erforderlich Aus Sicherheitsgründen funktioniert es nicht über
http://
, außer auflocalhost
. -
Gehashte Dateinamen Der Cache-Name kann den Dateinamen, die URL und Versionsinformationen enthalten.
-
Kommunikation mit Clients Verwenden Sie
postMessage
, um zwischen demService Worker
und dem JavaScript der Seite zu kommunizieren.
Zusammenfassung
Service Worker
ist eine grundlegende Technologie zur Offline-Unterstützung und zur Leistungsverbesserung von Webanwendungen. Indem Sie den grundlegenden Ablauf von Installation, Aktivierung und Fetch-Verarbeitung verstehen und geeignete Cache-Strategien umsetzen, können Sie Webanwendungen mit höherer Qualität erstellen.
Sie können den obigen Artikel mit Visual Studio Code auf unserem YouTube-Kanal verfolgen. Bitte schauen Sie sich auch den YouTube-Kanal an.