Erstellen Sie eine PWA mit Webpack und Workbox

Veröffentlicht: 2022-03-10
Kurze Zusammenfassung ↬ Dieses Tutorial hilft Ihnen dabei, eine App, die nicht offline funktioniert, in eine PWA umzuwandeln, die offline funktioniert und ein Symbol für verfügbare Updates anzeigt. Sie erfahren, wie Sie Assets mit Workbox vorab zwischenspeichern, dynamisches Caching handhaben und Aktualisierungen an Ihrer PWA handhaben. Folgen Sie uns und sehen Sie, wie Sie diese Techniken auch auf Ihrer Website anwenden können.

Eine Progressive Web App (PWA) ist eine Website, die mithilfe moderner Technologie App-ähnliche Erfahrungen im Web bereitstellt. Es ist ein Überbegriff für neue Technologien wie „Web App Manifest“, „Service Worker“ und mehr. Wenn diese Technologien zusammengefügt werden, können Sie mit Ihrer Website schnelle und ansprechende Benutzererlebnisse bereitstellen.

Dieser Artikel ist eine Schritt-für-Schritt-Anleitung zum Hinzufügen eines Servicemitarbeiters zu einer vorhandenen einseitigen Website. Der Servicemitarbeiter ermöglicht es Ihnen, Ihre Website offline zu betreiben und gleichzeitig Ihre Benutzer über Aktualisierungen Ihrer Website zu informieren. Bitte beachten Sie, dass dies auf einem kleinen Projekt basiert, das mit Webpack gebündelt ist, daher verwenden wir das Workbox Webpack-Plugin (Workbox v4).

Die Verwendung eines Tools zum Generieren Ihres Service Workers ist der empfohlene Ansatz, da Sie damit Ihren Cache effizient verwalten können. Wir werden Workbox verwenden – eine Reihe von Bibliotheken, die es einfach machen, Ihren Service Worker-Code zu generieren – um unseren Service Worker in diesem Tutorial zu generieren.

Abhängig von Ihrem Projekt können Sie Workbox auf drei verschiedene Arten verwenden:

  1. Es steht eine Befehlszeilenschnittstelle zur Verfügung, mit der Sie Workbox in jede Ihrer Anwendungen integrieren können.
  2. Ein Node.js-Modul ist verfügbar, mit dem Sie Workbox in jedes Node-Build-Tool wie Gulp oder Grunt integrieren können.
  3. Ein Webpack-Plugin ist verfügbar, mit dem Sie sich einfach in ein Projekt integrieren können, das mit Webpack erstellt wurde.

Webpack ist ein Modul-Bundler. Vereinfacht können Sie es sich als ein Tool vorstellen, das Ihre JavaScript-Abhängigkeiten verwaltet. Es ermöglicht Ihnen, JavaScript-Code aus Bibliotheken zu importieren und Ihr JavaScript in einer oder mehreren Dateien zu bündeln.

Mehr nach dem Sprung! Lesen Sie unten weiter ↓

Klonen Sie zunächst das folgende Repository auf Ihrem Computer:

 git clone [email protected]:jadjoubran/workbox-tutorial-v4.git cd workbox-tutorial-v4 npm install npm run dev

Navigieren Sie als Nächstes zu https://localhost:8080/ . Sie sollten die Währungs-App sehen können, die wir in diesem Tutorial verwenden werden:

Screenshot der Währungs-App, die wir in diesem Artikel erstellen.
„Währungen“ ist eine PWA, die Umrechnungsgebühren von Währungen gegenüber der Währung Euro (€) auflistet. (Große Vorschau)

Beginnen Sie mit einer App-Shell

Die Anwendungs-Shell (oder „App-Shell“) ist ein von nativen Apps inspiriertes Muster. Es wird dazu beitragen, Ihrer App ein nativeres Aussehen zu verleihen. Es stellt der App einfach ein Layout und eine Struktur ohne Daten bereit – ein Übergangsbildschirm, der darauf abzielt, das Ladeerlebnis Ihrer Web-App zu verbessern.

Hier sind einige Beispiele für App-Shells von nativen Apps:

Google Inbox-App-Shell
Google Inbox App Shell: Einige Millisekunden, bevor die E-Mails in die App-Shell geladen werden. (Große Vorschau)
Twitter-native App-Shell
Twitters native App auf Android: App-Shell mit Navigationsleiste, Tabs und Loader. (Große Vorschau)

Und hier sind Beispiele für App-Shells von PWAs:

App Shell von Twitters PWA
Die App-Shell von Twitters PWA (große Vorschau)
App Shell von Flipkarts PWA
Die App-Shell von Flipkarts PWA (große Vorschau)

Benutzer mögen das Ladeerlebnis von App-Shells, weil sie leere Bildschirme verachten. Ein leerer Bildschirm gibt dem Benutzer das Gefühl, dass die Website nicht geladen wird. Es gibt ihnen das Gefühl, als ob die Website feststeckt.

App-Shells versuchen, die Navigationsstruktur der App so schnell wie möglich zu zeichnen, z. B. die Navigationsleiste, die Registerkartenleiste sowie einen Loader, der anzeigt, dass der von Ihnen angeforderte Inhalt geladen wird.

Wie erstellt man also eine App-Shell?

Das App-Shell-Muster priorisiert das Laden von HTML, CSS und JavaScript, die zuerst gerendert werden. Das bedeutet, dass wir diesen Ressourcen die volle Priorität einräumen müssen, daher müssen Sie diese Assets einbetten. Um also eine App-Shell zu erstellen, müssen Sie lediglich HTML, CSS und JavaScript inline einfügen, die für die App-Shell verantwortlich sind. Natürlich sollte man nicht alles inlinen, sondern innerhalb einer Grenze von etwa 30 bis 40 KB insgesamt bleiben.

Sie können die eingebettete App-Shell in der index.html sehen. Sie können den Quellcode überprüfen, indem Sie die Datei index.html auschecken , und Sie können eine Vorschau im Browser anzeigen, indem Sie das Element <main> in den Entwicklungstools löschen:

Währungen PWA App Shell mit Navigationsleiste & Loader
Die App Shell, die wir in diesem Artikel erstellen. (Große Vorschau)

Funktioniert es offline?

Lassen Sie uns simulieren, offline zu gehen! Öffnen Sie DevTools, navigieren Sie zur Registerkarte „Netzwerk“ und aktivieren Sie das Kontrollkästchen „Offline“. Wenn Sie die Seite neu laden, sehen Sie, dass wir die Offline-Seite des Browsers erhalten.

Offline-Fehlerseite des Browsers
Die Anfrage an die Homepage ist fehlgeschlagen, daher sehen wir dies offensichtlich als Ergebnis. (Große Vorschau)

Das liegt daran, dass die anfängliche Anfrage an / (die die Datei index.html lädt) fehlschlägt, weil das Internet offline ist. Die einzige Möglichkeit für uns, uns von diesem Anforderungsfehler zu erholen, besteht darin, einen Servicemitarbeiter einzusetzen.

Lassen Sie uns die Anfrage ohne einen Servicemitarbeiter visualisieren:

Animation einer Netzwerkanfrage, die vom Browser des Clients ins Internet geht.
Die Netzwerkanforderung geht vom Browser zum Internet und zurück. (Icons von flaticon.com) (Große Vorschau)

Ein Service Worker ist ein programmierbarer Netzwerk-Proxy, was bedeutet, dass er sich zwischen Ihrer Webseite und dem Internet befindet. Auf diese Weise können Sie eingehende und ausgehende Netzwerkanforderungen steuern.

Animation einer vom Servicemitarbeiter abgefangenen Netzwerkanfrage.
Die Netzwerkanfrage wird vom Servicemitarbeiter abgefangen. (Icons von flaticon.com) (Große Vorschau)

Dies ist vorteilhaft, da wir diese fehlgeschlagene Anforderung jetzt an den Cache umleiten können (vorausgesetzt, wir haben den Inhalt im Cache).

Animation einer Netzwerkanforderung, die vom Servicemitarbeiter abgefangen und an den Cache umgeleitet wird.
Die Netzwerkanforderung wird an den Cache umgeleitet, wenn sie bereits im Cache vorhanden ist. (Icons von flaticon.com) (Große Vorschau)

Ein Service Worker ist auch eine Art Web Worker, was bedeutet, dass er getrennt von Ihrer Hauptseite ausgeführt wird und keinen Zugriff auf das window oder document hat.

Die App-Shell vorab zwischenspeichern

Damit unsere App offline funktioniert, beginnen wir mit dem Precaching der App-Shell.

Beginnen wir also mit der Installation des Webpack Workbox-Plugins:

 npm install --save-dev workbox-webpack-plugin

Dann öffnen wir unsere Datei index.js und registrieren den Service Worker:

 if ("serviceWorker" in navigator){ window.addEventListener("load", () => { navigator.serviceWorker.register("/sw.js"); }) }

Öffnen Sie als Nächstes die Datei webpack.config.js und konfigurieren Sie das Workbox-Webpack-Plugin:

 //add at the top const WorkboxWebpackPlugin = require("workbox-webpack-plugin"); //add inside the plugins array: plugins: [ … , new WorkboxWebpackPlugin.InjectManifest({ swSrc: "./src/src-sw.js", swDest: "sw.js" }) ]

Dadurch wird Workbox angewiesen, unsere Datei ./src/src-sw.js als Basis zu verwenden. Die generierte Datei heißt sw.js und befindet sich im dist -Ordner.

Erstellen Sie dann eine ./src/src-sw.js-Datei auf der Stammebene und schreiben Sie Folgendes hinein:

 workbox.precaching.precacheAndRoute(self.__precacheManifest);

Hinweis : Die Variable self.__precacheManifest wird aus einer Datei importiert, die von Workbox dynamisch generiert wird.

Jetzt können Sie Ihren Code mit npm run build erstellen, und Workbox generiert zwei Dateien im dist -Ordner:

  • precache-manifest.66cf63077c7e4a70ba741ee9e6a8da29.js
  • sw.js

Die sw.js importiert die Workbox vom CDN sowie das Precache-Manifest.[chunkhash].js .

 //precache-manifest.[chunkhash].js file self.__precacheManifest = (self.__precacheManifest || []).concat([ "revision": "ba8f7488757693a5a5b1e712ac29cc28", "url": "index.html" }, "url": "main.49467c51ac5e0cb2b58e.js" ]);

Das Precache-Manifest listet die Namen der Dateien auf, die von Webpack verarbeitet wurden und in Ihrem dist -Ordner landen. Wir werden diese Dateien verwenden, um sie im Browser vorab zwischenzuspeichern. Dies bedeutet, dass Ihre Website beim ersten Laden und Registrieren des Servicemitarbeiters diese Assets zwischenspeichert, damit sie beim nächsten Mal verwendet werden können.

Sie können auch feststellen, dass einige Einträge eine „Revision“ haben, während andere dies nicht tun. Das liegt daran, dass die Revision manchmal aus dem Chunkhash aus dem Dateinamen erschlossen werden kann. Schauen wir uns zum Beispiel den Dateinamen main.49467c51ac5e0cb2b58e.js genauer an. Es hat eine Überarbeitung im Dateinamen, nämlich chunkhash 49467c51ac5e0cb2b58e .

Dadurch kann Workbox nachvollziehen, wenn sich Ihre Dateien ändern, sodass nur die geänderten Dateien bereinigt oder aktualisiert werden, anstatt jedes Mal, wenn Sie eine neue Version Ihres Service Workers veröffentlichen, den gesamten Cache zu leeren.

Wenn Sie die Seite zum ersten Mal laden, wird der Service Worker installiert. Sie können das in DevTools sehen. Zuerst wird die Datei sw.js angefordert, die dann alle anderen Dateien anfordert. Sie sind deutlich mit dem Zahnradsymbol gekennzeichnet.

Screenshot der Registerkarte „Netzwerk“ von DevTools, wenn der Dienstmitarbeiter installiert wird.
Anfragen, die mit dem Symbol ️ gekennzeichnet sind, sind Anfragen, die vom Servicemitarbeiter initiiert wurden. (Große Vorschau)

Workbox wird also initialisiert und alle Dateien, die sich im Precache-Manifest befinden, vorab zwischengespeichert. Es ist wichtig, noch einmal zu überprüfen, dass Sie keine unnötigen Dateien in der Precache-Manifest- Datei haben, wie z. B. .map -Dateien oder Dateien, die nicht Teil der App-Shell sind.

Auf der Registerkarte Netzwerk können wir die Anfragen sehen, die vom Servicemitarbeiter kommen. Und wenn Sie jetzt versuchen, offline zu gehen, ist die App-Shell bereits vorab im Cache gespeichert, sodass sie auch dann funktioniert, wenn wir offline sind!

Screenshot der Registerkarte "Netzwerk" von Dev Tools mit fehlgeschlagenen API-Aufrufen.
API-Aufrufe schlagen fehl, wenn wir offline gehen. (Große Vorschau)

Dynamische Routen zwischenspeichern

Haben Sie bemerkt, dass die App-Shell funktioniert, wenn wir offline gegangen sind, aber nicht unsere Daten? Das liegt daran, dass diese API-Aufrufe nicht Teil der vorab zwischengespeicherten App-Shell sind. Wenn keine Internetverbindung besteht, schlagen diese Anfragen fehl und der Benutzer kann die Währungsinformationen nicht sehen.

Diese Anforderungen können jedoch nicht vorab zwischengespeichert werden, da ihr Wert von einer API stammt. Wenn Sie mehrere Seiten haben, möchten Sie außerdem nicht alle API-Anforderungen auf einmal zwischenspeichern. Stattdessen möchten Sie sie zwischenspeichern, wenn der Benutzer diese Seite besucht.

Wir nennen diese „dynamische Daten“. Sie enthalten häufig API-Aufrufe sowie Bilder und andere Elemente, die angefordert werden, wenn ein Benutzer eine bestimmte Aktion auf Ihrer Website ausführt (z. B. wenn er zu einer neuen Seite navigiert).

Sie können diese mit dem Routing-Modul von Workbox zwischenspeichern. Hier ist wie:

 //add in src/src-sw.js workbox.routing.registerRoute( /https:\/\/api\.exchangeratesapi\.io\/latest/, new workbox.strategies.NetworkFirst({ cacheName: "currencies", plugins: [ new workbox.expiration.Plugin({ maxAgeSeconds: 10 * 60 // 10 minutes }) ] }) );

Dadurch wird dynamisches Caching für jede Anforderungs-URL eingerichtet, die mit der URL https://api.exchangeratesapi.io/latest übereinstimmt.

Die Caching-Strategie, die wir hier verwendet haben, heißt NetworkFirst ; Es gibt zwei andere, die häufig verwendet werden:

  1. CacheFirst
  2. StaleWhileRevalidate

CacheFirst wird zuerst im Cache danach suchen. Wenn es nicht gefunden wird, wird es aus dem Netzwerk abgerufen. StaleWhileRevalidate geht gleichzeitig ins Netzwerk und in den Cache. Geben Sie die Antwort des Caches an die Seite zurück (während er im Hintergrund ist), verwendet er die neue Netzwerkantwort, um den Cache für die nächste Verwendung zu aktualisieren.

Für unseren Anwendungsfall mussten wir uns für NetworkFirst , da wir es mit Währungskursen zu tun haben, die sich sehr oft ändern. Wenn der Benutzer jedoch offline geht, können wir ihm zumindest die Kurse so anzeigen, wie sie vor 10 Minuten waren – deshalb haben wir das Expiration-Plugin verwendet, bei dem maxAgeSeconds auf 10 * 60 Sekunden eingestellt war.

App-Updates verwalten

Jedes Mal, wenn ein Benutzer Ihre Seite lädt, führt der Browser den Code navigator.serviceWorker.register aus, obwohl der Service Worker bereits installiert ist und ausgeführt wird. Dadurch kann der Browser erkennen, ob es eine neue Version des Service Workers gibt. Wenn der Browser feststellt, dass sich die Datei nicht geändert hat, überspringt er einfach den Registrierungsaufruf. Sobald sich diese Datei ändert, versteht der Browser, dass es eine neue Version des Service Workers gibt, und installiert daher den neuen Service Worker parallel zum derzeit ausgeführten Service Worker .

Es wird jedoch in der installed/waiting angehalten, da nur ein Dienstmitarbeiter gleichzeitig aktiviert werden kann.

Der Lebenszyklus eines Servicemitarbeiters: Geparst, installiert/wartend, aktiviert und redundant
Ein vereinfachter Lebenszyklus eines Servicemitarbeiters (Große Vorschau)

Erst wenn alle vom vorherigen Servicemitarbeiter kontrollierten Browserfenster geschlossen sind, ist die Aktivierung für den neuen Servicemitarbeiter sicher.

Sie können dies auch manuell steuern, indem skipWaiting() (oder self.skipWaiting() , da self der globale Ausführungskontext im Service Worker ist). Meistens sollten Sie dies jedoch erst tun, nachdem Sie den Benutzer gefragt haben, ob er das neueste Update erhalten möchte.

Glücklicherweise hilft uns workbox-window dabei. Es handelt sich um eine neue Fensterbibliothek, die in Workbox v4 eingeführt wurde und darauf abzielt, allgemeine Aufgaben auf der Seite des Fensters zu vereinfachen.

Beginnen wir mit der Installation wie folgt:

 npm install workbox-window

Als nächstes importieren Workbox am Anfang der Datei index.js :

 import { Workbox } from "workbox-window";

Dann ersetzen wir unseren Registrierungscode durch den folgenden:

 if ("serviceWorker" in navigator) { window.addEventListener("load", () => { const wb = new Workbox("/sw.js"); wb.register(); }); }

Wir finden dann die Update-Schaltfläche mit der ID App-Update und auf das workbox-waiting event warten:

 //add before the wb.register() const updateButton = document.querySelector("#app-update"); // Fires when the registered service worker has installed but is waiting to activate. wb.addEventListener("waiting", event => { updateButton.classList.add("show"); updateButton.addEventListener("click", () => { // Set up a listener that will reload the page as soon as the previously waiting service worker has taken control. wb.addEventListener("controlling", event => { window.location.reload(); }); // Send a message telling the service worker to skip waiting. // This will trigger the `controlling` event handler above. wb.messageSW({ type: "SKIP_WAITING" }); }); });

Dieser Code zeigt die Update-Schaltfläche an, wenn es ein neues Update gibt (also wenn sich der Servicemitarbeiter in einem Wartezustand befindet) und sendet eine SKIP_WAITING Nachricht an den Servicemitarbeiter.

Wir müssen die Service-Worker-Datei aktualisieren und das SKIP_WAITING Ereignis so behandeln, dass es skipWaiting :

 //add in src-sw.js addEventListener("message", event => { if (event.data && event.data.type === "SKIP_WAITING") { skipWaiting(); });

Führen Sie nun npm run dev und laden Sie die Seite neu. Gehen Sie in Ihren Code und aktualisieren Sie den Navbar-Titel auf „Navbar v2“. Laden Sie die Seite erneut und Sie sollten das Aktualisierungssymbol sehen können.

Einpacken

Unsere Website funktioniert jetzt offline und kann den Benutzer über neue Updates informieren. Bitte bedenken Sie jedoch, dass der wichtigste Faktor beim Erstellen einer PWA die Benutzererfahrung ist. Konzentrieren Sie sich immer darauf, Erfahrungen zu erstellen, die von Ihren Benutzern einfach zu verwenden sind. Wir als Entwickler neigen dazu, uns zu sehr für Technologie zu begeistern und vergessen am Ende oft unsere Benutzer.

Wenn Sie noch einen Schritt weiter gehen möchten, können Sie ein Web-App-Manifest hinzufügen, mit dem Ihre Benutzer die Website zu ihrem Startbildschirm hinzufügen können. Und wenn Sie mehr über Workbox erfahren möchten, finden Sie die offizielle Dokumentation auf der Workbox-Website.

Weiterführende Literatur zu SmashingMag:

  • Können Sie mit einer mobilen App oder einer PWA mehr Geld verdienen?
  • Ein umfassender Leitfaden für progressive Webanwendungen
  • Native und PWA: Entscheidungen, keine Herausforderer!
  • Erstellen einer PWA mit Angular 6