Damit Sie auch im Offlinemodus präsent sind, benötigt Ihre PWA eine Speicherverwaltung. Im Kapitel zum Caching haben Sie gelernt, dass der Cache eine Option zum Speichern von Daten auf einem Gerät ist. In diesem Kapitel erfahren Sie, wie Sie Offlinedaten verwalten, einschließlich Datenpersistenz, Limits und die verfügbaren Tools.
Speicher
Der Speicher umfasst nicht nur Dateien und Assets, sondern kann auch andere Datentypen enthalten. In allen Browsern, die PWAs unterstützen, sind die folgenden APIs für den On-Device-Speicher verfügbar:
- IndexedDB: Eine NoSQL-Objektspeicheroption für strukturierte Daten und Blobs (Binärdaten).
- WebStorage: Eine Möglichkeit, Schlüssel/Wert-Zeichenfolgenpaare unter Verwendung des lokalen Speichers oder Sitzungsspeichers zu speichern. Sie ist innerhalb eines Service Worker-Kontexts nicht verfügbar. Da diese API synchron ist, wird sie für komplexe Datenspeicher nicht empfohlen.
- Cache-Speicher: Wie im Caching-Modul beschrieben.
Sie können den gesamten Gerätespeicher mit der Storage Manager API auf unterstützten Plattformen verwalten. Die Cache Storage API und IndexedDB bieten für PWAs asynchronen Zugriff auf nichtflüchtigen Speicher und können über den Hauptthread, Web Worker und Service Worker aufgerufen werden. Beide tragen entscheidend dazu bei, dass PWAs auch dann zuverlässig funktionieren, wenn das Netzwerk instabil oder nicht vorhanden ist. Aber wann sollten sie verwendet werden?
Verwenden Sie die Cache Storage API für Netzwerkressourcen, d. h. Inhalte, auf die Sie über eine URL zugreifen können, z. B. HTML, CSS, JavaScript, Bilder, Videos und Audio.
Verwenden Sie IndexedDB zum Speichern strukturierter Daten. Dazu gehören Daten, die in NoSQL-ähnlicher Weise durchsuchbar oder kombinierbar sein müssen, sowie andere Daten wie nutzerspezifische Daten, die nicht unbedingt mit einer URL-Anfrage übereinstimmen. IndexedDB ist nicht für die Volltextsuche vorgesehen.
IndexedDB
Öffnen Sie zuerst eine Datenbank, um IndexedDB zu verwenden. Dadurch wird eine neue Datenbank erstellt, sofern noch keine vorhanden ist.
IndexedDB ist ein asynchrones API, nimmt jedoch einen Callback an, statt ein Promise zurückzugeben. Im folgenden Beispiel wird die idb-Bibliothek von Jake Archibald verwendet, ein winziger Promise-Wrapper für IndexedDB. Hilfsbibliotheken sind zur Verwendung von IndexedDB nicht erforderlich. Wenn Sie jedoch die Promise-Syntax verwenden möchten, ist die Bibliothek idb
eine Option.
Im folgenden Beispiel wird eine Datenbank für Kochrezepte erstellt.
Datenbank erstellen und öffnen
So öffnen Sie eine Datenbank:
- Erstellen Sie mit der Funktion
openDB
eine neue IndexedDB-Datenbank mit dem Namencookbook
. Da IndexedDB-Datenbanken versioniert sind, müssen Sie die Versionsnummer jedes Mal erhöhen, wenn Sie Änderungen an der Datenbankstruktur vornehmen. Der zweite Parameter ist die Datenbankversion. Im Beispiel ist der Wert auf 1 festgelegt. - Ein Initialisierungsobjekt, das einen
upgrade()
-Callback enthält, wird anopenDB()
übergeben. Die Callback-Funktion wird aufgerufen, wenn die Datenbank zum ersten Mal installiert oder auf eine neue Version aktualisiert wird. Dies ist der einzige Ort, an dem Aktionen ausgeführt werden können. Aktionen können das Erstellen neuer Objektspeicher (die Strukturen, die IndexedDB zum Organisieren von Daten verwendet) oder Indexe (die Sie durchsuchen möchten) umfassen. An dieser Stelle sollte auch die Datenmigration stattfinden. In der Regel enthält die Funktionupgrade()
eineswitch
-Anweisung ohnebreak
-Anweisungen, damit die einzelnen Schritte entsprechend der alten Version der Datenbank in der richtigen Reihenfolge ausgeführt werden können.
import { openDB } from 'idb';
async function createDB() {
// Using https://github.com/jakearchibald/idb
const db = await openDB('cookbook', 1, {
upgrade(db, oldVersion, newVersion, transaction) {
// Switch over the oldVersion, *without breaks*, to allow the database to be incrementally upgraded.
switch(oldVersion) {
case 0:
// Placeholder to execute when database is created (oldVersion is 0)
case 1:
// Create a store of objects
const store = db.createObjectStore('recipes', {
// The `id` property of the object will be the key, and be incremented automatically
autoIncrement: true,
keyPath: 'id'
});
// Create an index called `name` based on the `type` property of objects in the store
store.createIndex('type', 'type');
}
}
});
}
Im Beispiel wird ein Objektspeicher mit dem Namen recipes
in der cookbook
-Datenbank erstellt, wobei das Attribut id
als Indexschlüssel des Speichers festgelegt ist. Außerdem wird ein weiterer Index mit dem Namen type
erstellt, der auf dem Attribut type
basiert.
Sehen wir uns den soeben erstellten Objektspeicher an. Nachdem Sie Schemas zum Objektspeicher hinzugefügt und die Entwicklertools in Chromium-basierten Browsern oder den Web Inspector in Safari geöffnet haben, sollte Folgendes angezeigt werden:
Daten hinzufügen
IndexedDB verwendet Transaktionen. In Transaktionen werden Aktionen gruppiert, sodass sie eine Einheit bilden. Sie tragen dazu bei, dass die Datenbank immer in einem konsistenten Zustand ist. Sie sind auch wichtig, wenn mehrere Kopien Ihrer App ausgeführt werden, um gleichzeitiges Schreiben in dieselben Daten zu verhindern. So fügen Sie Daten hinzu:
- Starten Sie eine Transaktion und setzen Sie
mode
aufreadwrite
. - Rufen Sie den Objektspeicher ab, in dem Sie Daten hinzufügen können.
- Rufen Sie
add()
mit den Daten auf, die Sie speichern. Die Methode empfängt Daten in Wörterbuchform (als Schlüssel/Wert-Paare) und fügt sie dem Objektspeicher hinzu. Das Wörterbuch muss mithilfe von strukturiertem Klonen geklont werden können. Wenn Sie ein vorhandenes Objekt aktualisieren möchten, rufen Sie stattdessen die Methodeput()
auf.
Transaktionen haben ein done
-Promise, das aufgelöst wird, wenn die Transaktion erfolgreich abgeschlossen wurde, oder mit einem Transaktionsfehler abgelehnt wird.
Wie in der Dokumentation zur IDB-Bibliothek erläutert, gibt tx.done
beim Schreiben in die Datenbank an, dass der Commit für alle Elemente erfolgreich abgeschlossen wurde. Es ist jedoch sinnvoll, auf einzelne Vorgänge zu warten, damit Sie Fehler sehen können, die zum Scheitern der Transaktion führen.
// Using https://github.com/jakearchibald/idb
async function addData() {
const cookies = {
name: "Chocolate chips cookies",
type: "dessert",
cook_time_minutes: 25
};
const tx = await db.transaction('recipes', 'readwrite');
const store = tx.objectStore('recipes');
store.add(cookies);
await tx.done;
}
Nachdem Sie die Cookies hinzugefügt haben, befindet sich das Rezept zusammen mit anderen Rezepten in der Datenbank. Die ID wird von indexiertDB automatisch festgelegt und erhöht. Wenn Sie diesen Code zweimal ausführen, erhalten Sie zwei identische Cookie-Einträge.
Daten werden abgerufen…
So rufen Sie Daten von IndexedDB ab:
- Starten Sie eine Transaktion und geben Sie den oder die Objektspeicher sowie optional den Transaktionstyp an.
- Rufen Sie aus dieser Transaktion
objectStore()
auf. Geben Sie unbedingt den Namen des Objektspeichers an. - Rufen Sie
get()
mit dem gewünschten Schlüssel auf. Standardmäßig verwendet der Speicher seinen Schlüssel als Index.
// Using https://github.com/jakearchibald/idb
async function getData() {
const tx = await db.transaction('recipes', 'readonly')
const store = tx.objectStore('recipes');
// Because in our case the `id` is the key, we would
// have to know in advance the value of the id to
// retrieve the record
const value = await store.get([id]);
}
Speichermanager
Damit Netzwerkantworten richtig gespeichert und gestreamt werden können, müssen Sie wissen, wie Sie den Speicher Ihrer PWA verwalten.
Die Speicherkapazität wird von allen Speicheroptionen gemeinsam genutzt, einschließlich Cache Storage, IndexedDB, Web Storage und sogar der Service Worker-Datei und ihren Abhängigkeiten.
Der verfügbare Speicherplatz variiert jedoch von Browser zu Browser. Sie werden wahrscheinlich nicht aufgebraucht werden. können Websites in einigen Browsern
Megabyte und sogar Gigabyte an Daten speichern. Chrome erlaubt dem Browser beispielsweise, bis zu 80% des gesamten Speicherplatzes zu nutzen, und ein einzelner Ursprung kann bis zu 60% des gesamten Speicherplatzes belegen. Bei Browsern, die die Storage API unterstützen, können Sie sehen, wie viel Speicherplatz noch für Ihre Anwendung verfügbar ist, und wie viel Speicherplatz noch verfügbar ist.
Im folgenden Beispiel werden mithilfe der Storage API das geschätzte Kontingent und die Nutzung ermittelt. Anschließend werden die genutzten und die verbleibenden Byte in Prozent berechnet. Beachten Sie, dass navigator.storage
eine Instanz von StorageManager
zurückgibt. Es gibt eine separate Storage
-Oberfläche, die leicht verwechselt werden kann.
if (navigator.storage && navigator.storage.estimate) {
const quota = await navigator.storage.estimate();
// quota.usage -> Number of bytes used.
// quota.quota -> Maximum number of bytes available.
const percentageUsed = (quota.usage / quota.quota) * 100;
console.log(`You've used ${percentageUsed}% of the available storage.`);
const remaining = quota.quota - quota.usage;
console.log(`You can write up to ${remaining} more bytes.`);
}
In den Chromium-Entwicklertools können Sie das Kontingent Ihrer Website und den verwendeten Speicherplatz aufgeschlüsselt nach Auslastung sehen. Öffnen Sie dazu auf dem Tab Anwendung den Bereich Speicher.
In Firefox und Safari wird keine Zusammenfassung des gesamten Speicherkontingents und der gesamten Speichernutzung für den aktuellen Ursprung angezeigt.
Datenpersistenz
Sie können vom Browser auf kompatiblen Plattformen dauerhaften Speicher anfordern, um eine automatische Datenbereinigung nach Inaktivität oder bei Speicherauslastung zu vermeiden. Wenn diese Option gewährt wird, entfernt der Browser niemals Daten aus dem Speicher. Dieser Schutz umfasst die Service Worker-Registrierung, IndexedDB-Datenbanken und Dateien im Cache-Speicher. Beachten Sie, dass die Nutzer immer die Verantwortung dafür übernehmen und sie den Speicher jederzeit löschen können, auch wenn der Browser Ihnen nichtflüchtigen Speicher gewährt hat.
Rufen Sie StorageManager.persist()
auf, um nichtflüchtigen Speicher anzufordern. Wie zuvor erfolgt der Zugriff auf die StorageManager
-Schnittstelle über das Attribut navigator.storage
.
async function persistData() {
if (navigator.storage && navigator.storage.persist) {
const result = await navigator.storage.persist();
console.log(`Data persisted: ${result}`);
}
Sie können auch prüfen, ob im aktuellen Ursprung bereits nichtflüchtiger Speicher gewährt wurde, indem Sie StorageManager.persisted()
aufrufen. Firefox fordert vom Nutzer eine Berechtigung zur Verwendung des nichtflüchtigen Speichers an. Chromium-basierte Browser entscheiden anhand einer Heuristik, wie wichtig Inhalte für Nutzer sind, oder lehnen die Persistenz ab. Ein Kriterium für Google Chrome ist beispielsweise die PWA-Installation. Wenn der Nutzer im Betriebssystem ein Symbol für die PWA installiert hat, gewährt der Browser möglicherweise nichtflüchtigen Speicher.
API-Browser-Unterstützung
Webspeicher
Unterstützte Browser
- <ph type="x-smartling-placeholder">
- <ph type="x-smartling-placeholder">
- <ph type="x-smartling-placeholder">
- <ph type="x-smartling-placeholder">
Dateisystemzugriff
Unterstützte Browser
- <ph type="x-smartling-placeholder">
- <ph type="x-smartling-placeholder">
- <ph type="x-smartling-placeholder">
- <ph type="x-smartling-placeholder">
Speichermanager
Unterstützte Browser
- <ph type="x-smartling-placeholder">
- <ph type="x-smartling-placeholder">
- <ph type="x-smartling-placeholder">
- <ph type="x-smartling-placeholder">