Übersicht über fünf Browser-Speichermechanismen
Moderne Browser bieten 5 primäre Speichermechanismen, die jeweils für unterschiedliche Ziele und Anwendungsfälle konzipiert sind:
| Speicher |
Datentyp |
Kapazitätsgrenze |
Persistenz |
Thread-Zugriff |
| localStorage |
String-Schlüssel-Wert |
~5-10 MB |
Dauerhaft |
Nur Haupt-Thread |
| sessionStorage |
String-Schlüssel-Wert |
~5-10 MB |
Tab-Schließung |
Nur Haupt-Thread |
| IndexedDB |
Strukturierte Daten + Blob |
Hunderte MB~unbegrenzt |
Dauerhaft |
Haupt-Thread + Worker |
| Cache API |
Request/Response-Paare |
Geteilt mit IndexedDB-Kontingent |
Dauerhaft |
Haupt-Thread + Worker |
| OPFS |
Dateisystem |
Geteilt mit IndexedDB-Kontingent |
Dauerhaft |
Nur Worker (synchron) |
localStorage und sessionStorage
Grundlegende Verwendung
localStorage.setItem('theme', 'dark');
const theme = localStorage.getItem('theme');
localStorage.removeItem('theme');
localStorage.clear();
for (let i = 0; i < localStorage.length; i++) {
const key = localStorage.key(i);
console.log(key, localStorage.getItem(key));
}
Wesentliche Unterschiede
| Merkmal |
localStorage |
sessionStorage |
| Lebensdauer |
Dauerhaft |
Beim Tab-Schließen gelöscht |
| Gültigkeitsbereich |
Geteilt zwischen Tabs gleicher Herkunft |
Nur aktueller Tab |
| Typische Verwendung |
Benutzereinstellungen, Tokens |
Formularentwürfe, Seitenzustand |
Kapazitätserkennung
function testLocalStorageQuota() {
const testKey = '__quota_test__';
let data = '';
try {
for (let i = 0; i < 1024; i++) data += 'a'.repeat(1024);
for (let mb = 1; mb < 20; mb++) {
localStorage.setItem(testKey, data.repeat(mb));
}
} catch (e) {
console.log('localStorage quota exceeded');
} finally {
localStorage.removeItem(testKey);
}
}
Hinweise
- Synchrones Blockieren: Lesen/Schreiben blockiert den Haupt-Thread; große Daten beeinträchtigen die Leistung
- Nur Strings: Objekte benötigen
JSON.stringify/JSON.parse
- Storage-Ereignis: Löst das
storage-Ereignis in anderen Tabs gleicher Herkunft bei Änderung aus
IndexedDB
Grundlegende Verwendung
const openRequest = indexedDB.open('myDatabase', 1);
openRequest.onupgradeneeded = (event) => {
const db = event.target.result;
const store = db.createObjectStore('users', { keyPath: 'id' });
store.createIndex('name', 'name', { unique: false });
};
openRequest.onsuccess = (event) => {
const db = event.target.result;
const tx = db.transaction('users', 'readwrite');
const store = tx.objectStore('users');
store.add({ id: 1, name: 'Alice', email: 'alice@example.com' });
store.get(1).onsuccess = (e) => {
console.log(e.target.result);
};
};
Erweiterte Funktionen
| Funktion |
Beschreibung |
| Transaktionen |
Garantiert Atomarität, Lese-/Schreibtrennung |
| Indizes |
Effiziente Abfragen auf Nicht-Schlüsselfeldern |
| Cursor |
Große Datensätze iterieren ohne alles gleichzeitig zu laden |
| Blob-Speicherung |
File- und Blob-Objekte direkt speichern |
| Worker-Zugriff |
Verfügbar in Service Workers und Web Workers |
Vereinfacht mit der idb-Bibliothek
import { openDB } from 'idb';
const db = await openDB('myDatabase', 1, {
upgrade(db) {
db.createObjectStore('users', { keyPath: 'id' });
}
});
await db.put('users', { id: 1, name: 'Alice' });
const user = await db.get('users', 1);
const allUsers = await db.getAll('users');
await db.delete('users', 1);
Cache API
Grundlegende Verwendung
const cache = await caches.open('v1');
await cache.put('/api/data', new Response(JSON.stringify({ foo: 'bar' })));
const response = await cache.match('/api/data');
const data = await response.json();
await cache.delete('/api/data');
Mit Service Worker
self.addEventListener('fetch', (event) => {
event.respondWith(
caches.match(event.request).then((cached) => {
if (cached) return cached;
return fetch(event.request).then((response) => {
const clone = response.clone();
caches.open('v1').then((cache) => {
cache.put(event.request, clone);
});
return response;
});
})
);
});
Vergleich von Caching-Strategien
| Strategie |
Beschreibung |
Anwendungsfall |
| Cache First |
Zuerst Cache, Netzwerk bei Fehlen |
Statische Assets, Schriftarten |
| Network First |
Zuerst Netzwerk, Cache als Fallback |
API-Daten, häufig aktualisierter Inhalt |
| Stale While Revalidate |
Cache zurückgeben, im Hintergrund aktualisieren |
Nicht-kritische APIs, Bilder |
| Cache Only |
Nur Cache |
Offline-Seiten, vorgecachte Ressourcen |
| Network Only |
Nur Netzwerk |
Non-GET-Anfragen, Echtzeitdaten |
OPFS (Origin Private File System)
Grundlegende Verwendung
const root = await navigator.storage.getDirectory();
const fileHandle = await root.getFileHandle('data.bin', { create: true });
const writable = await fileHandle.createWritable();
await writable.write(new Uint8Array([1, 2, 3, 4]));
await writable.close();
const file = await fileHandle.getFile();
const buffer = await file.arrayBuffer();
Synchroner Zugriff in Workern
// In einem Worker — synchrones OPFS (extrem schnell)
const root = await navigator.storage.getDirectory();
const fileHandle = await root.getFileHandle('large.bin', { create: true });
const accessHandle = await fileHandle.createSyncAccessHandle();
const buffer = new ArrayBuffer(4);
accessHandle.write(buffer, { at: 0 });
accessHandle.flush();
accessHandle.close();
OPFS vs File System Access API
| Merkmal |
OPFS |
File System Access API |
| Benutzerberechtigung |
Nicht erforderlich |
Benutzer muss Verzeichnis auswählen |
| Sichtbarkeit |
Browser-internes virtuelles Dateisystem |
Echtes Betriebssystem-Dateisystem |
| Worker-Sync |
Unterstützt |
Nicht unterstützt |
| Persistenz |
Dauerhaft |
Hängt von Benutzerberechtigung ab |
| Anwendungsfall |
Hochleistungs-Temporärdateiverarbeitung |
Dateieditoren, Export |
Speicherkontingent und Persistenz
Verfügbares Kontingent abfragen
const estimate = await navigator.storage.estimate();
console.log(`Used: ${(estimate.usage / 1024 / 1024).toFixed(2)} MB`);
console.log(`Quota: ${(estimate.quota / 1024 / 1024).toFixed(2)} MB`);
Persistenz anfordern
const persisted = await navigator.storage.persist();
if (persisted) {
console.log('Storage persisted, will not be auto-evicted');
} else {
console.log('Storage may be cleared under pressure');
}
Browser können nicht-persistenten Speicher unter Druck automatisch entfernen. Rufen Sie navigator.storage.persist() auf, um Persistenz anzufordern.
Entscheidungsleitfaden
What do you need to store?
├── Simple key-value (< 5MB)
│ ├── Cross-tab sharing → localStorage
│ └── Current tab only → sessionStorage
├── Structured data / many records
│ └── IndexedDB
├── HTTP request/response caching
│ └── Cache API
└── Large files / binary / high-performance I/O
└── OPFS
Praxisbeispiele
| Szenario |
Empfohlener Speicher |
Grund |
| BenutzertHEMA-Einstellung |
localStorage |
Einfaches Schlüssel-Wert, seitenübergreifend |
| Formularentwurf |
sessionStorage |
Temporär, tab-bezogen |
| Offline-Tool-Daten |
IndexedDB |
Strukturiert, indizierte Abfragen |
| Statische Asset-Cache |
Cache API |
Für Request/Response konzipiert |
| Videoverarbeitungs-Temporärdateien |
OPFS |
Synchrone Worker-E/A, hohe Leistung |
Sicherheitsaspekte
- XSS-Risiko: localStorage ist durch XSS lesbar — niemals sensible Tokens speichern
- HttpOnly-Cookies: Auth-Tokens sollten HttpOnly + Secure-Cookies verwenden
- Speicherverschlüsselung: Sensible Daten vor dem Speichern mit JWT oder Hash verschlüsseln
- Same-Origin-Isolierung: Alle Speicher folgen der Same-Origin-Richtlinie; Cross-Origin-Zugriff ist blockiert
Zusammenfassung
Der Browser-Speicher hat sich von einem einzelnen localStorage zu einem vollständigen System entwickelt, das Schlüssel-Wert-Paare, strukturierte Daten, HTTP-Caching und Dateisysteme abdeckt. Der Schlüssel zur Auswahl ist die Übereinstimmung mit den Datenmerkmalen: einfache Konfiguration → localStorage, strukturierte Daten → IndexedDB, HTTP-Caching → Cache API, hochperformante Datei-E/A → OPFS.
Verwenden Sie den Cookie-Parser zur Überprüfung der Cookie-Konfiguration, das JWT-Tool zur Überprüfung der Token-Sicherheit und das Hash-Tool zum Hashen sensibler Daten.