Eines der besten Dinge, das Sie 2022 für Ihre Website tun können, ist das Hinzufügen eines Service Workers, falls Sie noch keinen eingerichtet haben. Service Worker verleihen Ihrer Website Superkräfte. Heute möchte ich Ihnen einige der erstaunlichen Dinge zeigen, die sie tun können, und Ihnen eine einfache Vorlage geben, die Sie sofort auf Ihrer Website verwenden können.
Was sind Service Worker?
Ein Service Worker ist ein spezieller Typ einer JavaScript-Datei, der wie eine Middleware für Ihre Website fungiert. Jede Anfrage, die von der Website kommt, und jede Antwort, die sie zurückerhält, durchläuft zuerst die Service Worker-Datei. Service Worker haben auch Zugriff auf einen speziellen Cache, in dem sie Antworten und Assets lokal speichern können.
Zusammen ermöglichen Ihnen diese Funktionen...
- Häufig aufgerufene Assets aus Ihrem lokalen Cache anstelle des Netzwerks auszuliefern, um den Datenverbrauch zu reduzieren und die Leistung zu verbessern.
- Zugriff auf kritische Informationen (oder sogar Ihre gesamte Website oder App) zu ermöglichen, wenn der Besucher offline geht.
- Wichtige Assets und API-Antworten vorab abzurufen, damit sie bereit sind, wenn der Benutzer sie benötigt.
- Fallback-Assets als Reaktion auf HTTP-Fehler bereitzustellen.
Kurz gesagt, Service Worker ermöglichen es Ihnen, schnellere und widerstandsfähigere Weberlebnisse zu erstellen.
Im Gegensatz zu regulären JavaScript-Dateien haben Service Worker keinen Zugriff auf das DOM. Sie laufen auch auf einem eigenen Thread und blockieren daher nicht die Ausführung anderer JavaScript-Dateien. Service Worker sind darauf ausgelegt, vollständig asynchron zu sein.
Sicherheit
Da Service Worker jede Anfrage und Antwort für Ihre Website oder App abfangen, haben sie einige wichtige Sicherheitsbeschränkungen.
Service Worker befolgen eine Same-Origin-Policy.
Sie können Ihren Service Worker nicht von einem CDN oder einem Drittanbieter ausführen. Er muss auf derselben Domäne gehostet werden, auf der er ausgeführt werden soll.
Service Worker funktionieren nur auf Websites mit einem installierten SSL-Zertifikat.
Viele Webhosts bieten SSL-Zertifikate kostenlos oder gegen eine geringe Gebühr an. Wenn Sie mit der Kommandozeile vertraut sind, können Sie auch mit Let’s Encrypt kostenlos eines installieren.
Es gibt eine Ausnahme von der SSL-Zertifikatsanforderung für localhost-Tests, aber Sie können Ihren Service Worker nicht über das file://-Protokoll ausführen. Sie müssen einen lokalen Server ausführen.
Hinzufügen eines Service Workers zu Ihrer Website oder Webanwendung
Um einen Service Worker zu verwenden, müssen wir ihn zuerst beim Browser registrieren. Sie können einen Service Worker mit der Methode navigator.serviceWorker.register() registrieren. Übergeben Sie den Pfad zur Service Worker-Datei als Argument.
navigator.serviceWorker.register('sw.js');
Sie können dies in einer externen JavaScript-Datei ausführen, aber ich ziehe es vor, es direkt in einem script-Element inline in meinem HTML auszuführen, damit es so schnell wie möglich läuft.
Im Gegensatz zu anderen Arten von JavaScript-Dateien funktionieren Service Worker nur für das Verzeichnis, in dem sie sich befinden (und alle seine Unterverzeichnisse). Eine Service Worker-Datei unter /js/sw.js würde nur für Dateien im /js-Verzeichnis funktionieren. Daher sollten Sie Ihre Service Worker-Datei im Stammverzeichnis Ihrer Website platzieren.
Obwohl Service Worker eine hervorragende Browserunterstützung haben, ist es eine gute Idee sicherzustellen, dass der Browser sie unterstützt, bevor Sie Ihr Registrierungsskript ausführen.
if (navigator && navigator.serviceWorker) {
navigator.serviceWorker.register('sw.js');
}
Nachdem der Service Worker installiert wurde, kann der Browser ihn aktivieren. Dies geschieht normalerweise nur, wenn...
- kein Service Worker derzeit aktiv ist, oder
- der Benutzer die Seite neu lädt.
Der Service Worker wird nicht ausgeführt und fängt keine Anfragen ab, bis er aktiviert ist.
Auf Anfragen in einem Service Worker hören
Sobald der Service Worker aktiv ist, kann er beginnen, Anfragen abzufangen und andere Aufgaben auszuführen. Wir können mit self.addEventListener() und dem fetch-Ereignis auf Anfragen hören.
// Listen for request events
self.addEventListener('fetch', function (event) {
// Do stuff...
});
Innerhalb des Ereignis-Listeners ist die Eigenschaft event.request das Anfrageobjekt selbst. Der Einfachheit halber können wir es in der Variable request speichern.
Bestimmte Versionen des Chromium-Browsers haben einen Fehler, der einen Fehler auslöst, wenn die Seite in einem neuen Tab geöffnet wird. Glücklicherweise gibt es einen einfachen Fix von Paul Irish, den ich in alle meine Service Worker aufnehme, nur für den Fall.
// Listen for request events
self.addEventListener('fetch', function (event) {
// Get the request
let request = event.request;
// Bug fix
// https://stackoverflow.com/a/49719964
if (event.request.cache === 'only-if-cached' && event.request.mode !== 'same-origin') return;
});
Sobald Ihr Service Worker aktiv ist, wird jede einzelne Anfrage an ihn gesendet und mit dem fetch-Ereignis abgefangen.
Service Worker-Strategien
Sobald Ihr Service Worker installiert und aktiviert ist, können Sie Anfragen und Antworten abfangen und diese auf verschiedene Arten verarbeiten. Es gibt zwei primäre Strategien, die Sie in Ihrem Service Worker verwenden können.
- Netzwerk-zuerst. Bei einem Netzwerk-zuerst-Ansatz leiten Sie Anfragen an das Netzwerk weiter. Wenn die Anfrage nicht gefunden wird oder keine Netzwerkverbindung besteht, suchen Sie die Anfrage im Service Worker-Cache.
- Offline-zuerst. Bei einem Offline-zuerst-Ansatz prüfen Sie zuerst, ob ein angefordertes Asset im Service Worker-Cache vorhanden ist. Wenn es nicht gefunden wird, senden Sie die Anfrage an das Netzwerk.
Netzwerk-zuerst und Offline-zuerst Ansätze arbeiten Hand in Hand. Sie werden wahrscheinlich Ansätze mischen und anpassen, je nach Art des angeforderten Assets.
Offline-zuerst eignet sich hervorragend für große Assets, die sich nicht oft ändern: CSS, JavaScript, Bilder und Schriftarten. Netzwerk-zuerst eignet sich besser für häufig aktualisierte Assets wie HTML und API-Anfragen.
Strategien zum Caching von Assets
Wie bekommen Sie Assets in den Cache Ihres Browsers? Sie werden normalerweise zwei verschiedene Ansätze verwenden, abhängig von den Asset-Typen.
- Pre-caching bei der Installation. Jede Website und Webanwendung hat eine Reihe von Kern-Assets, die auf fast jeder Seite verwendet werden: CSS, JavaScript, ein Logo, Favicon und Schriftarten. Sie können diese während des
install-Ereignisses vorab cachen und sie mit einem Offline-zuerst-Ansatz bereitstellen, wann immer sie angefordert werden. - Cache beim Surfen. Ihre Website oder App hat wahrscheinlich Assets, die nicht bei jedem Besuch oder von jedem Besucher abgerufen werden; Dinge wie Blogbeiträge und Bilder, die zu Artikeln gehören. Für diese Assets möchten Sie sie möglicherweise in Echtzeit cachen, während der Besucher darauf zugreift.
Sie können diese gecachten Assets dann entweder standardmäßig oder als Fallback bereitstellen, je nach Ihrem Ansatz.
Implementierung von Netzwerk-zuerst und Offline-zuerst Strategien in Ihrem Service Worker
Innerhalb eines fetch-Ereignisses in Ihrem Service Worker gibt die Methode request.headers.get('Accept') den MIME-Typ für den Inhalt zurück. Wir können dies verwenden, um zu bestimmen, für welchen Dateityp die Anfrage bestimmt ist. MDN hat eine Liste gängiger Dateien und ihrer MIME-Typen. HTML-Dateien haben beispielsweise den MIME-Typ text/html.
Wir können den gesuchten Dateityp als Argument in die Methode String.includes() übergeben und if-Anweisungen verwenden, um basierend auf dem Dateityp unterschiedlich zu antworten.
// Listen for request events
self.addEventListener('fetch', function (event) {
// Get the request
let request = event.request;
// Bug fix
// https://stackoverflow.com/a/49719964
if (event.request.cache === 'only-if-cached' && event.request.mode !== 'same-origin') return;
// HTML files
// Network-first
if (request.headers.get('Accept').includes('text/html')) {
// Handle HTML files...
return;
}
// CSS & JavaScript
// Offline-first
if (request.headers.get('Accept').includes('text/css') || request.headers.get('Accept').includes('text/javascript')) {
// Handle CSS and JavaScript files...
return;
}
// Images
// Offline-first
if (request.headers.get('Accept').includes('image')) {
// Handle images...
}
});
Netzwerk-zuerst
Innerhalb jeder if-Anweisung verwenden wir die Methode event.respondWith(), um die an den Browser zurückgesendete Antwort zu ändern.
Für Assets, die einen Netzwerk-zuerst-Ansatz verwenden, nutzen wir die fetch()-Methode, übergeben die request, um die Anfrage für die HTML-Datei durchzureichen. Wenn sie erfolgreich zurückgegeben wird, return wir die response in unserer Callback-Funktion. Dies ist dasselbe Verhalten wie beim Nichtvorhandensein eines Service Workers.
Wenn ein Fehler auftritt, können wir Promise.catch() verwenden, um die Antwort zu ändern, anstatt die Standard-Browser-Fehlermeldung anzuzeigen. Wir können die Methode caches.match() verwenden, um nach dieser Seite zu suchen, und sie anstelle der Netzwerk-response returnen.
// Send the request to the network first
// If it's not found, look in the cache
event.respondWith(
fetch(request).then(function (response) {
return response;
}).catch(function (error) {
return caches.match(request).then(function (response) {
return response;
});
})
);
Offline-zuerst
Für Assets, die einen Offline-zuerst-Ansatz verwenden, prüfen wir zuerst im Browser-Cache mit der Methode caches.match(). Wenn eine Übereinstimmung gefunden wird, return wir sie. Andernfalls verwenden wir die Methode fetch(), um die request an das Netzwerk weiterzuleiten.
// Check the cache first
// If it's not found, send the request to the network
event.respondWith(
caches.match(request).then(function (response) {
return response || fetch(request).then(function (response) {
return response;
});
})
);
Pre-caching von Kern-Assets
Innerhalb eines install-Ereignis-Listeners im Service Worker können wir die Methode caches.open() verwenden, um einen Service Worker-Cache zu öffnen. Wir übergeben den gewünschten Namen für den Cache, app, als Argument.
Der Cache ist auf Ihre Domäne beschränkt und auf diese beschränkt. Andere Websites können nicht darauf zugreifen, und wenn sie einen Cache mit demselben Namen haben, sind die Inhalte vollständig getrennt.
Die Methode caches.open() gibt ein Promise zurück. Wenn bereits ein Cache mit diesem Namen existiert, wird das Promise damit aufgelöst. Wenn nicht, wird der Cache zuerst erstellt und dann aufgelöst.
// Listen for the install event
self.addEventListener('install', function (event) {
event.waitUntil(caches.open('app'));
});
Als Nächstes können wir eine then()-Methode an unsere caches.open()-Methode mit einer Callback-Funktion anhängen.
Um Dateien zum Cache hinzuzufügen, müssen wir sie anfordern, was wir mit dem Konstruktor new Request() tun können. Wir können die Methode cache.add() verwenden, um die Datei zum Service Worker-Cache hinzuzufügen. Dann returnen wir das cache-Objekt.
Wir möchten, dass das install-Ereignis wartet, bis wir unsere Datei gecacht haben, bevor es abgeschlossen wird. Wickeln wir also unseren Code in die Methode event.waitUntil().
// Listen for the install event
self.addEventListener('install', function (event) {
// Cache the offline.html page
event.waitUntil(caches.open('app').then(function (cache) {
cache.add(new Request('offline.html'));
return cache;
}));
});
Ich finde es hilfreich, ein Array mit den Pfaden zu all meinen Kern-Dateien zu erstellen. Dann, innerhalb des install-Ereignis-Listeners, nachdem ich meinen Cache geöffnet habe, kann ich jedes Element durchlaufen und es hinzufügen.
let coreAssets = [
'/css/main.css',
'/js/main.js',
'/img/logo.svg',
'/img/favicon.ico'
];
// On install, cache some stuff
self.addEventListener('install', function (event) {
// Cache core assets
event.waitUntil(caches.open('app').then(function (cache) {
for (let asset of coreAssets) {
cache.add(new Request(asset));
}
return cache;
}));
});
Cache beim Surfen
Ihre Website oder App hat wahrscheinlich Assets, die nicht bei jedem Besuch oder von jedem Besucher abgerufen werden; Dinge wie Blogbeiträge und Bilder, die zu Artikeln gehören. Für diese Assets möchten Sie sie möglicherweise in Echtzeit cachen, während der Besucher darauf zugreift. Bei nachfolgenden Besuchen können Sie sie direkt aus dem Cache laden (mit einem Offline-zuerst-Ansatz) oder als Fallback bereitstellen, wenn das Netzwerk ausfällt (mit einem Netzwerk-zuerst-Ansatz).
Wenn eine fetch()-Methode eine erfolgreiche response zurückgibt, können wir die Methode Response.clone() verwenden, um eine Kopie davon zu erstellen.
Als Nächstes können wir die Methode caches.open() verwenden, um unseren Cache zu öffnen. Dann verwenden wir die Methode cache.put(), um die kopierte Antwort im Cache zu speichern, und übergeben die request und eine copy der response als Argumente. Da dies eine asynchrone Funktion ist, wickeln wir unseren Code in die Methode event.waitUntil(). Dies verhindert, dass das Ereignis endet, bevor wir unsere copy im Cache gespeichert haben. Sobald die copy gespeichert ist, können wir die response wie gewohnt returnen.
/explanation Wir verwenden cache.put() anstelle von cache.add(), da wir bereits eine response haben. Die Verwendung von cache.add() würde einen weiteren Netzwerkaufruf tätigen.
// HTML files
// Network-first
if (request.headers.get('Accept').includes('text/html')) {
event.respondWith(
fetch(request).then(function (response) {
// Create a copy of the response and save it to the cache
let copy = response.clone();
event.waitUntil(caches.open('app').then(function (cache) {
return cache.put(request, copy);
}));
// Return the response
return response;
}).catch(function (error) {
return caches.match(request).then(function (response) {
return response;
});
})
);
}
Alles zusammenfügen
Ich habe eine Copy-Paste-Vorlage für Sie auf GitHub zusammengestellt. Fügen Sie Ihre Kern-Assets zum Array coreAssets hinzu und registrieren Sie es auf Ihrer Website, um loszulegen.
Wenn Sie nichts weiter tun, wird dies eine enorme Verbesserung für Ihre Website im Jahr 2022 sein.
Aber es gibt noch so viel mehr, was Sie mit Service Workern tun können. Es gibt fortgeschrittene Caching-Strategien für APIs. Sie können eine Offline-Seite mit kritischen Informationen bereitstellen, wenn ein Besucher seine Netzwerkverbindung verliert. Sie können aufgeblähte Caches bereinigen, während der Benutzer surft.
Jeremy Keiths Buch, Going Offline, ist eine großartige Einführung in Service Worker. Wenn Sie die Dinge auf die nächste Stufe bringen und sich mit Progressive Web Apps beschäftigen möchten, taucht Jason Grigsbys Buch in die verschiedenen Strategien ein, die Sie verwenden können.
Und für eine pragmatische Tiefenanalyse, die Sie in etwa einer Stunde abschließen können, habe ich auch einen Kurs und ein E-Book zu Service Workern mit vielen Codebeispielen und einem Projekt, an dem Sie arbeiten können.
Ich verstehe nicht, warum Sie
cache.add(new Request('offline.html'));statt des einfacherencache.add('offline.html');schreiben. Was fügtRequest()hinzu?Außerdem könnten Sie
cache.addAll(coreAssets);verwenden, ohne selbst zu loopen, wenn SieRequest()nicht verwenden würden.Um ehrlich zu sein, bin ich mir nicht zu 100 % sicher, warum ich angefangen habe,
new Request()hinzuzufügen. Ich kann mich nicht erinnern, ob ich früher Probleme hatte, dass Dinge nicht gecacht wurden, oder ob ich diese Angewohnheit einfach von jemand anderem übernommen habe.Entschuldigung!
Ja, aber…
cache.addAll()schlägt fehl, wenn ein einzelnes Asset einen Fehler auslöst. Durch das Loopen wird ein fehlerhaftes Asset einfach ignoriert.Das Scheitern, wenn eines der Assets einen Fehler auslöst, ist tatsächlich der Zweck, da wir versuchen, die Dinge konsistent zu halten.
Danke für diesen interessanten Beitrag! Aber ich frage mich, macht das der Browser nicht schon? Warum würde dieser Code eine Leistungssteigerung gegenüber dem bewirken, was der Browser tut?
Nicht mit dem gleichen Kontrollniveau, nein.
Browser machen etwas Caching, und Sie können mit einigen Serverkonfigurationen Ablauf-Header setzen.
Aber um Dinge wie Offline-Seiten bereitzustellen oder vollständig Offline-Websites/Apps zu erstellen, benötigen Sie einen Service Worker.
Service Worker ermöglichen es Ihnen auch, Dateien vorab abzurufen und zu cachen, bevor ein Besucher darauf zugreift, und bieten die Möglichkeit, Anfragen abzufangen und zu ändern.
Dieser Artikel war eine Einführung, aber Service Worker sind weitaus leistungsfähiger als das, was hier nur behandelt wird.
Ich wurde von einem Service Worker richtiggehend in die Klemme gebracht, als ich versuchte, zu einem neuen Hosting-Anbieter zu migrieren.
Der Service Worker war bei den meisten Benutzern der Website angekommen, und als diese Benutzer dann Seiten aufriefen
Damals wusste ich nichts über Service Worker, und so habe ich mich im Kreis gedreht und versucht herauszufinden, warum mein neues Hosting nicht funktionierte. (Benutzer bekamen immer noch Seiten, aber nicht die, die ich ihnen geben wollte.)
Meine Lösung (nachdem ich etwas darüber gelernt hatte) war, einen neuen SW zu erstellen, der den alten überschrieb und deinstallierte.
Wenn Sie sich für den SW-Weg entscheiden (oder entscheiden werden)... überlegen Sie, was passieren könnte, wenn Sie das Hosting wechseln müssen.
Auch wenn Sie eine neue Domain für sich kaufen, ist es hilfreich zu überlegen, welche Service Worker möglicherweise bereits bereitgestellt wurden und welche Auswirkungen sie auf Ihre neue Domain haben könnten.
Was Sie beschreiben, ist eher eine Gefahr beim Caching von HTML-Dateien mit einem Offline-zuerst-Ansatz als Service Worker als eine breite Strategie.
Es ist jedoch definitiv ein häufiger Fehler, der alle Arten von seltsamen Problemen verursachen kann, wenn Sie nicht wissen, was passiert.
Für einen kostenlosen, exzellenten, gamifizierten Überblick über Service Workies empfehle ich dringend Service Workies on Mastery Games.
Hinweis: Am besten in Chrome. Firefox hat einige Probleme, die es unmöglich machen, bestimmte Stufen zu bestehen.
Einige Zitate von der Seite (eigentlich fast alles)
Der Service Worker ist nicht nur zum Caching und für PWAs da. Sie können ihn verwenden, um Dateien im Browser zu generieren. Zum Beispiel aus IndexedDB. Das kann verwendet werden, um binäre Blobs oder Textdateien zwischen dem Hauptthread und dem SW auszutauschen.
Mehr dazu können Sie in diesem Artikel lesen
https://itnext.io/how-to-create-web-server-in-browser-ffaa371d2b53