`Service Worker` in JavaScript

`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 &amp; 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 und push.

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 (die Service Worker-Datei) zu registrieren.
  • Sie können während der Registrierung then für die Erfolgsbehandlung und catch für die Fehlerbehandlung nutzen.
  • registration.scope repräsentiert den Pfadbereich (Scope), der vom Service 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/ vom Service 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 der Service 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:.

  1. Eine neue sw.js wird erkannt.
  2. Das install-Ereignis wird ausgelöst.
  3. Es wird gewartet, bis der vorherige Service Worker inaktiv ist.
  4. Das activate-Ereignis wird ausgelöst.
  5. Es wird auf den neuen Service Worker umgeschaltet.
  6. 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 auf localhost.

  • Gehashte Dateinamen Der Cache-Name kann den Dateinamen, die URL und Versionsinformationen enthalten.

  • Kommunikation mit Clients Verwenden Sie postMessage, um zwischen dem Service 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.

YouTube Video