Diese zweiteilige Reihe ist eine sanfte Einführung in die Offline-Webentwicklung. Eine Webanwendung offline zum Laufen zu bringen, ist überraschend knifflig und erfordert, dass viele Dinge vorhanden sind und korrekt funktionieren. Wir werden all diese Teile auf hoher Ebene mit funktionierenden Beispielen behandeln. Dieser Beitrag ist ein Überblick, aber es gibt zahlreiche detailliertere Ressourcen, die überall aufgelistet sind.
Artikelserie
- Die Einrichtung (Sie sind hier!)
- Die Implementierung
Grundlegender Ansatz
Ich werde intensiv die JavaScript-Syntax async/await verwenden. Sie wird in allen großen Browsern und Node unterstützt und vereinfacht Promise-basierte Codes erheblich. Der obige Link erklärt async gut, aber kurz gesagt, sie ermöglichen es Ihnen, ein Promise aufzulösen und direkt im Code mit await auf seinen Wert zuzugreifen, anstatt .then aufzurufen und im Callback auf den Wert zuzugreifen, was oft zu der gefürchteten „Rechtsverschiebung“ führt.
Was bauen wir?
Wir werden ein bestehendes Booklist-Projekt erweitern, um die Bücher des aktuellen Benutzers mit IndexedDB zu synchronisieren und eine vereinfachte Offline-Seite zu erstellen, die auch dann angezeigt wird, wenn der Benutzer keine Netzwerkverbindung hat.
Beginnend mit einem Service Worker
Das einzig Nicht-verhandelbare, das Sie für die Offline-Entwicklung benötigen, ist ein Service Worker. Ein Service Worker ist ein Hintergrundprozess, der unter anderem Netzwerkanfragen abfangen, umleiten, durch Rückgabe gecachter Antworten unterbrechen oder normal ausführen und mit der Antwort benutzerdefinierte Dinge tun kann, wie z. B. Caching.
Grundlegendes Caching
Wahrscheinlich das erste, grundlegendste, aber wirkungsvollste, was Sie mit einem Service Worker tun werden, ist, Ihre Anwendungsressourcen zu cachen. Service Worker und der von ihm verwendete Cache sind extrem Low-Level-Primitive; alles ist manuell. Um Ihre Ressourcen richtig zu cachen, müssen Sie sie abrufen und einem Cache hinzufügen. Sie müssen aber auch Änderungen an diesen Ressourcen verfolgen. Sie verfolgen, wann sie sich ändern, entfernen die vorherige Version und rufen die neue ab und aktualisieren sie.
In der Praxis bedeutet dies, dass Ihr Service-Worker-Code als Teil eines Build-Schritts generiert werden muss, der Ihre Dateien hasht und eine Datei generiert, die Änderungen zwischen Versionen erkennt und Caches bei Bedarf aktualisiert.
Abstraktionen zur Rettung
Dies ist extrem mühsamer und fehleranfälliger Code, den Sie wahrscheinlich niemals selbst schreiben möchten. Glücklicherweise haben einige kluge Leute Abstraktionen geschrieben, um zu helfen: nämlich sw-precache und sw-toolbox von den großartigen Leuten bei Google. Beachten Sie, dass Google diese Tools inzwischen zugunsten des neueren Workbox abgesetzt hat. Ich habe meinen Code noch nicht migriert, da sw-* so gut funktioniert, aber in jedem Fall sind die Ideen dieselben und die Konvertierung ist angeblich einfach. Und es ist erwähnenswert, dass sw-precache derzeit etwa 30.000 Downloads **pro Tag** hat, sodass es immer noch weit verbreitet ist.
Hallo Welt, sw-precache
Legen wir sofort los. Wir verwenden webpack, und da es ein Plugin gibt, schauen wir uns das zuerst an.
// inside your webpack config
new SWPrecacheWebpackPlugin({
mergeStaticsConfig: true,
filename: "service-worker.js",
staticFileGlobs: [ //static resources to cache
"static/bootstrap/css/bootstrap-booklist-build.css",
...
],
ignoreUrlParametersMatching: /./,
stripPrefixMulti: { //any paths that need adjusting
"static/": "react-redux/static/",
...
},
...
})
Standardmäßig werden ALLE von webpack erstellten Bundles im Voraus gecached. Wir stellen auch manuell einige Pfade zu statischen Ressourcen bereit, die ich im staticFileGlobs-Eigenschaft gecacht haben möchte, und ich passe einige Pfade in stripPrefixMulti an.
// inside your webpack config
const getCache = ({ name, pattern, expires, maxEntries }) => ({
urlPattern: pattern,
handler: "cacheFirst",
options: {
cache: {
maxEntries: maxEntries || 500,
name: name,
maxAgeSeconds: expires || 60 * 60 * 24 * 365 * 2 //2 years
},
successResponses: /0|[123].*/
}
});
new SWPrecacheWebpackPlugin({
...
runtimeCaching: [ //pulls in sw-toolbox and caches dynamically based on a pattern
getCache({ pattern: /^https:\/\/images-na.ssl-images-amazon.com/, name: "amazon-images1" }),
getCache({ pattern: /book\/searchBooks/, name: "book-search", expires: 60 * 7 }), //7 minutes
...
]
})
Das Hinzufügen des runtimeCaching-Abschnitts zu unserem SWPrecacheWebpackPlugin ruft sw-toolbox auf und ermöglicht uns, URLs, die einem bestimmten Muster entsprechen, dynamisch nach Bedarf zu cachen – wobei getCache hilft, den Boilerplate-Code auf ein Minimum zu reduzieren.
Hallo Welt, sw-toolbox
Die gesamte generierte Service-Worker-Datei ist ziemlich groß, aber schauen wir uns nur einen kleinen Teil an, nämlich einen der obigen dynamischen Caches.
toolbox.router.get(/^https:\/\/images-na.ssl-images-amazon.com/, toolbox.cacheFirst, {
cache: { maxEntries: 500, name: "amazon-images1", maxAgeSeconds: 63072000 },
successResponses: /0|[123].*/
});
sw-toolbox hat uns ein schönes, High-Level-Router-Objekt zur Verfügung gestellt, das wir im MVC-Stil verwenden können, um verschiedene URL-Anfragen abzufangen. Wir werden dies bald für die Offline-Funktionalität verwenden.
Vergessen Sie nicht, den Service Worker zu registrieren
Und natürlich ist die Existenz der oben generierten Service-Worker-Datei allein nutzlos; sie muss registriert werden. Der Code sieht so aus, aber stellen Sie sicher, dass er entweder in einem onload-Listener oder an einer anderen Stelle enthalten ist, die garantiert nach dem Laden der Seite ausgeführt wird.
if ("serviceWorker" in navigator) {
navigator.serviceWorker.register("/service-worker.js");
}
Da haben wir es! Wir haben einen grundlegenden Service Worker zum Laufen gebracht, der unsere Anwendungsressourcen cacht. Schalten Sie morgen wieder ein, wenn wir ihn erweitern, um die Offline-Funktionalität zu unterstützen.
Artikelserie
- Die Einrichtung (Sie sind hier!)
- Die Implementierung
Service Worker werden wirklich nur in Chrome und Firefox unterstützt; IE und Safari unterstützen sie nicht. Immer noch ein weiter Weg von einer guten Cross-Browser-Lösung für Offline-Apps.
Leider ja, obwohl Apple angeblich mit der Arbeit daran begonnen hat. Hoffentlich haben wir sie bald auf iOS.
Hallo, ich habe versucht, nach „JavaScript Rightward Drift“ zu googeln, und finde nichts. Könnten Sie das näher erläutern?
Es hat viele Namen :)
Callback Hell, Rechtsverschiebung, Pyramide des Grauens usw.
http://callbackhell.com/