Es gibt viele verschiedene Möglichkeiten, Daten im Browser zu speichern. Welche Option ist für Ihre Anforderungen am besten geeignet?
Internetverbindungen können unterwegs unzuverlässig oder nicht vorhanden sein. Deshalb sind Offline-Unterstützung und zuverlässige Leistung gängige Funktionen in Progressive Web-Apps. Selbst in perfekten WLAN-Umgebungen kann die umsichtige Verwendung von Caching und anderen Speichertechniken die Nutzerfreundlichkeit erheblich verbessern. Es gibt mehrere Möglichkeiten, statische Anwendungsressourcen (HTML, JavaScript, CSS, Bilder usw.) und Daten (Nutzerdaten, Nachrichtenartikel usw.) im Cache zu speichern. Aber welche Lösung ist die beste? Wie viel können Sie speichern? Wie verhindern Sie, dass sie entfernt wird?
Was sollte ich verwenden?
Hier ist eine allgemeine Empfehlung zum Speichern von Ressourcen:
- Verwenden Sie für die Netzwerkressourcen, die zum Laden Ihrer App erforderlich sind, die Cache Storage API (Teil von Service Workern).
- Verwenden Sie für dateibasierte Inhalte das Origin Private File System (OPFS).
- Für andere Daten verwenden Sie IndexedDB (mit einem Promises-Wrapper).
IndexedDB, das OPFS und die Cache Storage API werden in allen modernen Browsern unterstützt.
Sie sind asynchron und blockieren den Hauptthread nicht. Es gibt aber auch eine synchrone Variante des OPFS, die ausschließlich in Web Workern verfügbar ist. Sie sind über das window-Objekt, Web-Worker und Service-Worker zugänglich und können daher überall in Ihrem Code verwendet werden.
Wie sieht es mit anderen Speichermechanismen aus?
Es gibt mehrere andere Speichermechanismen im Browser, die jedoch nur eingeschränkt verwendet werden können und erhebliche Leistungsprobleme verursachen können.
SessionStorage ist tabspezifisch und auf die Lebensdauer des Tabs beschränkt. Es kann nützlich sein, um kleine Mengen sitzungsspezifischer Informationen zu speichern, z. B. einen IndexedDB-Schlüssel. Sie sollte mit Vorsicht verwendet werden, da sie synchron ist und den Hauptthread blockiert. Er ist auf etwa 5 MB begrenzt und kann nur Strings enthalten. Da sie tabspezifisch ist, kann nicht über Web- oder Service-Worker darauf zugegriffen werden.
LocalStorage sollte vermieden werden, da es synchron ist und den Hauptthread blockiert. Er ist auf etwa 5 MB begrenzt und kann nur Strings enthalten. Auf LocalStorage kann nicht über Web-Worker oder Service-Worker zugegriffen werden.
Cookies haben ihre Berechtigung, sollten aber nicht zur Speicherung verwendet werden. Cookies werden mit jeder HTTP-Anfrage gesendet. Wenn Sie mehr als eine kleine Datenmenge speichern, erhöht sich die Größe jeder Webanfrage erheblich. Sie sind synchron und können nicht von Web Workern aufgerufen werden. Wie LocalStorage und SessionStorage sind Cookies auf Strings beschränkt.
Die File System Access API wurde entwickelt, um Nutzern das Lesen und Bearbeiten von Dateien in ihrem lokalen Dateisystem zu ermöglichen. Der Nutzer muss die Berechtigung erteilen, bevor eine Seite eine lokale Datei lesen oder schreiben kann. Die Berechtigungen bleiben nicht über Sitzungen hinweg erhalten, es sei denn, ein Datei-Handle wird in IndexedDB im Cache gespeichert. Die File System Access API eignet sich am besten für Anwendungsfälle wie Editoren, bei denen Sie eine Datei öffnen, ändern und dann möglicherweise die Änderungen in der Datei speichern müssen.
Die File System API und die FileWriter API bieten Methoden zum Lesen und Schreiben von Dateien in einem Sandbox-Dateisystem. Sie ist zwar asynchron, wird aber nicht empfohlen, da sie nur in Chromium-basierten Browsern verfügbar ist.
Wie viel kann ich speichern?
Kurz gesagt: Eine Menge, mindestens ein paar hundert Megabyte und potenziell Hunderte von Gigabyte oder mehr. Die Browserimplementierungen variieren, aber die Menge des verfügbaren Speichers hängt in der Regel von der Menge des auf dem Gerät verfügbaren Speichers ab.
- Chrome erlaubt dem Browser, bis zu 80 % des gesamten Speicherplatzes zu nutzen. Ein Ursprung kann bis zu 60 % des gesamten Speicherplatzes belegen. Mit der StorageManager API können Sie das maximal verfügbare Kontingent ermitteln. Bei anderen Chromium-basierten Browsern kann dies anders sein.
- Im Inkognitomodus reduziert Chrome den Speicherplatz, den ein Ursprung nutzen kann, auf etwa 5 % des gesamten Speicherplatzes.
- Wenn der Nutzer in Chrome die Option „Cookies und Websitedaten löschen, wenn alle Fenster geschlossen werden“ aktiviert hat, wird das Speicherkontingent erheblich auf maximal etwa 300 MB reduziert.
- Firefox erlaubt dem Browser, bis zu 50 % des freien Speicherplatzes zu nutzen. Eine eTLD+1-Gruppe (z.B.
example.com,www.example.comundfoo.bar.example.com) darf bis zu 2 GB verwenden. Mit der StorageManager API kannst du ermitteln, wie viel Speicherplatz noch verfügbar ist. - Safari (sowohl Desktop- als auch Mobilversion) erlaubt etwa 1 GB. Wenn das Limit erreicht ist, wird der Nutzer von Safari aufgefordert, das Limit in Schritten von 200 MB zu erhöhen. Ich konnte dazu keine offizielle Dokumentation finden.
- Wenn eine PWA in Safari auf Mobilgeräten dem Startbildschirm hinzugefügt wird, wird ein neuer Speichercontainer erstellt und es werden keine Daten zwischen der PWA und Safari auf Mobilgeräten geteilt. Sobald das Kontingent für eine installierte PWA erreicht ist, gibt es anscheinend keine Möglichkeit, zusätzlichen Speicherplatz anzufordern.
Wenn eine Website in der Vergangenheit einen bestimmten Schwellenwert für gespeicherte Daten überschritten hat, wurde der Nutzer vom Browser aufgefordert, die Berechtigung zur Verwendung von mehr Daten zu erteilen. Wenn der Ursprung beispielsweise mehr als 50 MB verwendet hat, wird der Nutzer vom Browser aufgefordert, bis zu 100 MB zu speichern. Anschließend wird er in Schritten von 50 MB noch einmal gefragt.
Die meisten modernen Browser fragen den Nutzer heute nicht mehr und erlauben einer Website, das ihr zugewiesene Kontingent zu nutzen. Die Ausnahme scheint Safari zu sein, das eine Aufforderung anzeigt, wenn das Speicherkontingent überschritten wird, und um Erlaubnis bittet, das zugewiesene Kontingent zu erhöhen. Wenn ein Ursprung versucht, mehr als das zugewiesene Kontingent zu verwenden, schlagen weitere Versuche, Daten zu schreiben, fehl.
Wie kann ich prüfen, wie viel Speicherplatz verfügbar ist?
In vielen Browsern können Sie mit der StorageManager API ermitteln, wie viel Speicherplatz für den Ursprung verfügbar ist und wie viel Speicherplatz er verwendet. Sie gibt die Gesamtzahl der von IndexedDB und der Cache API verwendeten Byte an und ermöglicht die Berechnung des ungefähren verbleibenden Speicherplatzes.
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.`);
}
Sie müssen Fehler aufgrund von Kontingentüberschreitungen abfangen (siehe unten). In einigen Fällen kann das verfügbare Kontingent den tatsächlich verfügbaren Speicherplatz überschreiten.
Prüfen
Während der Entwicklung können Sie die Entwicklertools Ihres Browsers verwenden, um die verschiedenen Speichertypen zu prüfen und alle gespeicherten Daten zu löschen.
In Chrome 88 wurde eine neue Funktion hinzugefügt, mit der Sie das Speicherkontingent der Website im Bereich „Speicher“ überschreiben können. Mit dieser Funktion können Sie verschiedene Geräte simulieren und das Verhalten Ihrer Apps in Szenarien mit geringer Speicherplatzverfügbarkeit testen. Gehen Sie zu Anwendung und dann zu Speicher, aktivieren Sie das Kästchen Benutzerdefiniertes Speicherkontingent simulieren und geben Sie eine gültige Zahl ein, um das Speicherkontingent zu simulieren.
Was passiert, wenn ich mein Kontingent überschreite?
Was sollten Sie tun, wenn Sie Ihr Kontingent überschreiten? Am wichtigsten ist, dass Sie Schreibfehler immer abfangen und beheben, unabhängig davon, ob es sich um einen QuotaExceededError oder etwas anderes handelt. Entscheiden Sie dann je nach App-Design, wie Sie damit umgehen möchten.
Sie können beispielsweise Inhalte löschen, auf die lange nicht zugegriffen wurde, Daten nach Größe entfernen oder Nutzern die Möglichkeit geben, selbst auszuwählen, was sie löschen möchten.
Sowohl IndexedDB als auch die Cache API geben einen DOMError mit dem Namen QuotaExceededError aus, wenn Sie das verfügbare Kontingent überschritten haben.
IndexedDB
Wenn der Ursprung sein Kontingent überschritten hat, schlagen Versuche, in IndexedDB zu schreiben, fehl. Der onabort()-Handler der Transaktion wird aufgerufen und ein Ereignis wird übergeben.
Das Ereignis enthält einen DOMException im Attribut „error“. Wenn Sie den Fehler name prüfen, wird QuotaExceededError zurückgegeben.
const transaction = idb.transaction(['entries'], 'readwrite');
transaction.onabort = function(event) {
const error = event.target.error; // DOMException
if (error.name == 'QuotaExceededError') {
// Fallback code goes here
}
};
Cache API
Wenn der Ursprung sein Kontingent überschritten hat, werden Versuche, in die Cache API zu schreiben, mit einem QuotaExceededError DOMException abgelehnt.
try {
const cache = await caches.open('my-cache');
await cache.add(new Request('/sample1.jpg'));
} catch (err) {
if (error.name === 'QuotaExceededError') {
// Fallback code goes here
}
}
Wie funktioniert die Bereinigung?
Webspeicher wird in zwei Kategorien unterteilt: „Best Effort“ und „Persistent“. „Best Effort“ bedeutet, dass der Speicher vom Browser gelöscht werden kann, ohne den Nutzer zu unterbrechen. Er ist jedoch weniger langlebig für langfristige oder kritische Daten. Nichtflüchtiger Speicher wird nicht automatisch gelöscht, wenn der Speicherplatz knapp wird. Der Nutzer muss diesen Speicher manuell über die Browsereinstellungen leeren.
Standardmäßig fallen die Daten einer Website (einschließlich IndexedDB, Cache API usw.) in die Kategorie „Best Effort“. Das bedeutet, dass der Browser die Websitedaten nach eigenem Ermessen entfernen kann, z. B. wenn der Gerätespeicher knapp wird, es sei denn, eine Website hat nichtflüchtigen Speicher angefordert.
Die Richtlinie für die Entfernung von Best-Effort-Pods lautet:
- Chromium-basierte Browser beginnen mit dem Löschen von Daten, wenn der Speicherplatz knapp wird. Dabei werden zuerst alle Websitedaten des am weitesten in der Vergangenheit verwendeten Ursprungs gelöscht, dann die des nächsten, bis der Browser nicht mehr über dem Limit liegt.
- Firefox beginnt mit dem Löschen von Daten, wenn der verfügbare Speicherplatz voll ist. Dabei werden zuerst alle Websitedaten vom am weitesten in der Vergangenheit verwendeten Ursprung gelöscht, dann vom nächsten, bis der Browser das Limit nicht mehr überschreitet.
- Safari hat zuvor keine Daten entfernt, aber vor Kurzem eine neue siebentägige Obergrenze für alle beschreibbaren Speicher eingeführt (siehe unten).
Ab iOS und iPadOS 13.4 und Safari 13.1 unter macOS gilt für alle per Skript beschreibbaren Speicher, einschließlich IndexedDB, Service Worker-Registrierung und Cache API, eine Obergrenze von sieben Tagen. Das bedeutet, dass Safari alle Inhalte nach sieben Tagen der Safari-Nutzung aus dem Cache entfernt, wenn der Nutzer nicht mit der Website interagiert. Diese Richtlinie zum Entfernen gilt nicht für installierte PWAs, die dem Startbildschirm hinzugefügt wurden. Weitere Informationen finden Sie im WebKit-Blog unter Full Third-Party Cookie Blocking and More.
Storage-Buckets
Die Kernidee der Storage Buckets API besteht darin, Websites die Möglichkeit zu geben, mehrere Speicher-Buckets zu erstellen, wobei der Browser jeden Bucket unabhängig von anderen Buckets löschen kann. So können Entwickler die Priorisierung für die Bereinigung festlegen, damit die wichtigsten Daten nicht gelöscht werden.
Bonus: Warum einen Wrapper für IndexedDB verwenden?
IndexedDB ist eine Low-Level-API, die vor der Verwendung umfangreich eingerichtet werden muss. Das kann besonders beim Speichern von Daten mit geringer Komplexität mühsam sein. Im Gegensatz zu den meisten modernen Promise-basierten APIs ist sie ereignisbasiert. Promise-Wrapper wie idb für IndexedDB verbergen einige der leistungsstarken Funktionen, aber vor allem die komplexen Mechanismen (z.B. Transaktionen, Schemaversionierung), die mit der IndexedDB-Bibliothek einhergehen.
Bonus: SQLite Wasm
Nachdem Web SQL eingestellt und aus Chrome entfernt wurde, hat Google mit den Betreibern der beliebten SQLite-Datenbank zusammengearbeitet, um einen Ersatz für Web SQL auf Basis von SQLite anzubieten. Weitere Informationen zur Verwendung finden Sie unter SQLite Wasm im Browser, unterstützt durch das Origin Private Dateisystem.
Fazit
Die Zeiten, in denen der Speicherplatz begrenzt war und Nutzer immer mehr Daten speichern mussten, sind vorbei. Websites können praktisch alle Ressourcen und Daten speichern, die sie für die Ausführung benötigen. Mit der StorageManager API können Sie ermitteln, wie viel Speicherplatz Ihnen zur Verfügung steht und wie viel Sie bereits belegt haben. Mit nichtflüchtigem Speicher können Sie die Daten vor dem Löschen schützen, es sei denn, der Nutzer entfernt sie.
Zusätzliche Ressourcen
- Best Practices für IndexedDB
- Chrome-Webspeicher und Kontingentkonzepte (in englischer Sprache)
Vielen Dank
Besonderer Dank gilt Jarryd Goodman, Phil Walton, Eiji Kitamura, Daniel Murphy, Darwin Huang, Josh Bell, Marijn Kruisselbrink und Victor Costan für die Überprüfung dieses Leitfadens. Vielen Dank an Eiji Kitamura, Addy Osmani und Marc Cohen, die die Originalartikel verfasst haben, auf denen dieser Artikel basiert. Eiji hat ein hilfreiches Tool namens Browser Storage Abuser geschrieben, mit dem das aktuelle Verhalten validiert werden kann. So können Sie so viele Daten wie möglich speichern und die Speicherlimits in Ihrem Browser sehen. Vielen Dank an François Beaufort, der Safari untersucht hat, um die Speicherlimits zu ermitteln, und an Thomas Steiner, der Informationen zum privaten Dateisystem des Ursprungs, zu Speicher-Buckets und zu SQLite Wasm hinzugefügt und 2024 ein allgemeines Inhaltsupdate durchgeführt hat.