Abschnitt 17 · Lektion 3
sessionStorage und die Grenzen
sessionStorage hat dieselben Methoden wie localStorage, dieselbe Textregel und dasselbe null bei fehlenden Schlüsseln. Alles aus den letzten beiden Lektionen gilt unverändert.
Der einzige Unterschied ist die Lebensdauer.
Der Unterschied
<p>Zwei Speicher, dieselben Methoden, getrennte Inhalte.</p> body {
font-family: system-ui, sans-serif;
max-width: 40rem;
margin: 0;
padding: 1rem;
line-height: 1.6;
color: #1c1917;
}
p {
margin: 0 0 0.75rem;
} localStorage.setItem("thema", "dunkel");
sessionStorage.setItem("entwurf", "Halb getippter Text");
console.log("local thema:", localStorage.getItem("thema"));
console.log("session thema:", sessionStorage.getItem("thema"));
console.log("local entwurf:", localStorage.getItem("entwurf"));
console.log("session entwurf:", sessionStorage.getItem("entwurf"));
console.log("Einträge in local:", localStorage.length);
console.log("Einträge in session:", sessionStorage.length);
// Die Methoden sind identisch. Nur der Kasten ist ein anderer.
sessionStorage.removeItem("entwurf");
console.log("Nach removeItem:", sessionStorage.getItem("entwurf")); localStorage bleibt, bis jemand ihn löscht. Über Tage, über Neustarts, über Browserversionen hinweg.
sessionStorage gilt nur für diesen einen Tab und nur, solange er offen ist. Ein Neuladen übersteht er. Ein neuer Tab auf derselben Seite bekommt einen eigenen, leeren. Und wenn der Tab zugeht, ist er weg.
Beachte im ersten Beispiel, dass die beiden Kästen nichts voneinander wissen. Ein thema im einen ist im anderen nicht da, obwohl der Schlüssel gleich heißt. In der Eingabezeile der Console kannst du beide nebeneinander befragen: localStorage.length antwortet mit 1, sessionStorage.length nach dem removeItem mit 0.
Der Kasten in dieser Lektion ist ein Ersatz
Das gehört an dieser Stelle gesagt, weil dieser Abschnitt sonst an einer Stelle etwas verspricht, das er nicht hält.
Die Seite hier läuft in einem abgeriegelten Rahmen ohne eigene Adresse, und in so einem Rahmen wirft der echte Browserspeicher bei jedem Zugriff, schon beim bloßen Lesen. Ohne Ersatz gäbe es diesen Abschnitt also gar nicht. Die Lernplattform stellt deshalb einen eigenen Kasten hin, mit derselben Schnittstelle: setItem, getItem, removeItem, clear, length, key, dazu localStorage.thema und delete localStorage.thema. Alles, was du hier lernst, gilt unverändert.
Drei Unterschiede solltest du kennen:
Jede Aufgabe und jedes Beispiel hat einen eigenen Kasten. Frag im ersten Beispiel Object.keys(localStorage), dann im dritten dieselbe Zeile. Einmal steht dort ein Schlüssel, einmal zwei. Das ist Absicht: Sonst läge in jeder Aufgabe der Kram aus der vorigen.
Er überlebt weniger, als du denkst. Ein Klick auf Seite neu laden ändert nichts an seinem Inhalt, ein Wechsel in eine andere Lektion und zurück ebenso wenig. Weg ist er, wenn du diese Seite selbst neu lädst oder den Tab schließt. Und der Knopf Zurücksetzen über dem Editor leert ihn zusammen mit deinem Code.
In den Entwicklerwerkzeugen deines Browsers steht davon nichts. Dort findest du unter dem Speicher dieser Seite nur die zwei Einträge aus Lektion 17.1, lern:queue und theme. Deine thema-Zeile von eben ist da nicht, und das ist kein Fehler, sondern genau der Punkt.
Und was sich hier gar nicht zeigen lässt, ist der Unterschied in der Lebensdauer zwischen den beiden Kästen. Es gibt keinen zweiten Tab, den man öffnen könnte. Du siehst die gleiche Schnittstelle und die getrennten Inhalte, nicht aber das Verschwinden. Probier das in einem echten Tab aus, dort ist es in zehn Sekunden nachgestellt.
Wann nimmst du welchen
Drei Beispiele, an denen die Entscheidung klar wird:
Das gewählte Thema gehört in localStorage. Wer die Seite morgen wieder aufmacht, will dieselbe Einstellung vorfinden.
Der halb getippte Text in einem Formular gehört in sessionStorage. Er soll ein versehentliches Neuladen überstehen und nicht drei Wochen.
Ein mehrstufiges Formular gehört in sessionStorage. Wer in einem zweiten Tab dasselbe Formular öffnet, soll dort nicht die Zwischenstände des ersten vorfinden.
Die Faustregel: Alles, was zu einem Arbeitsvorgang gehört, in die Sitzung. Alles, was zu einer Person gehört, dauerhaft.
Die erste Grenze: Platz
<p>Der Speicher hat ein Ende, und er sagt Bescheid.</p> body {
font-family: system-ui, sans-serif;
max-width: 40rem;
margin: 0;
padding: 1rem;
line-height: 1.6;
color: #1c1917;
}
p {
margin: 0 0 0.75rem;
} function ablegen(zeichen) {
try {
localStorage.setItem("gross", "x".repeat(zeichen));
console.log("Hat gepasst:", zeichen, "Zeichen");
return true;
} catch (fehler) {
console.warn("Abgelehnt bei", zeichen, "Zeichen");
console.warn("Name des Fehlers:", fehler.name);
return false;
}
}
ablegen(1000);
ablegen(1024 * 1024);
ablegen(6 * 1024 * 1024);
// Der abgelehnte Versuch hat nichts kaputtgemacht: der
// vorherige Wert steht unverändert da.
console.log("Länge des Eintrags:", localStorage.getItem("gross").length); Der Speicher hat ein Ende. Wo genau, ist von Browser zu Browser verschieden, aber rund fünf Megabyte pro Origin sind die übliche Größenordnung, und das gilt für localStorage und sessionStorage getrennt.
Was darüber hinausgeht, wird nicht etwa still verworfen: setItem wirft, und der Fehler trägt den Namen QuotaExceededError.
Das ist ein Fall, in dem ein try/catch seinen Platz hat, denn es gibt eine sinnvolle Reaktion darauf. Alte Einträge aufräumen, weniger speichern oder dem Nutzer sagen, dass etwas nicht gemerkt werden konnte. Was nicht geht, ist es zu ignorieren: Der Wert ist dann nicht gespeichert, und beim nächsten Lesen fehlt er einfach.
Der Deckel gilt für den ganzen Kasten, nicht für den einzelnen Eintrag. Das siehst du im zweiten Beispiel in drei Zeilen. Nach dem Lauf liegt dort ein Eintrag mit einem Megabyte; localStorage.getItem("gross").length in der Eingabezeile antwortet mit 1048576. Leg jetzt mit localStorage.setItem("zweit", "y".repeat(3 * 1024 * 1024)) drei weitere Megabyte dazu. Das geht noch durch. Nochmal zwei Megabyte darauf, und die Antwort lautet QuotaExceededError: Der Speicher ist voll., obwohl dieser eine Eintrag kleiner ist als der, der eben gepasst hat. Gerechnet wird die Summe.
Fünf Megabyte klingen nach viel. Für Einstellungen sind sie es auch. Für zwischengespeicherte Antworten aus dem Netz sind sie nach ein paar Tagen voll.
Die zweite Grenze: alles ist offen
<p>Ein zweites Stück Code, das nichts mit dem ersten zu tun hat, liest alles mit.</p> body {
font-family: system-ui, sans-serif;
max-width: 40rem;
margin: 0;
padding: 1rem;
line-height: 1.6;
color: #1c1917;
}
p {
margin: 0 0 0.75rem;
} // Teil eins der Anwendung legt etwas ab.
localStorage.setItem("thema", "dunkel");
localStorage.setItem("zugangstoken", "gehE1m-und-w1chtig");
// Teil zwei weiß davon nichts und sieht trotzdem alles.
// Genauso sieht es ein eingebundenes fremdes Skript.
function allesAuslesen() {
const gefunden = {};
for (let i = 0; i < localStorage.length; i++) {
const schluessel = localStorage.key(i);
gefunden[schluessel] = localStorage.getItem(schluessel);
}
return gefunden;
}
console.log("Gefunden:", JSON.stringify(allesAuslesen(), null, 2));
console.warn("Und das Token steht offen da."); Das ist die wichtigere Grenze, und sie ist keine technische Einschränkung, sondern eine Eigenschaft des Speichers.
Jedes Skript, das auf deiner Seite läuft, kann alles lesen. Dein eigenes, das eines Analyse-Werkzeugs, das eines Werbenetzwerks, und jedes, das jemand über eine Sicherheitslücke einschleust. Es gibt keine Rechte, keine getrennten Bereiche und keinen Schutz.
Die Eingabezeile der Console ist an dieser Stelle genau das fremde Skript, vor dem die Lektion warnt. Öffne das dritte Beispiel und tipp localStorage.getItem("zugangstoken"). Die Antwort lautet gehE1m-und-w1chtig. Kein Passwort davor, keine Rückfrage, eine Zeile. Object.keys(localStorage) daneben zeigt gleich die ganze Liste, ["thema", "zugangstoken"], und dazu braucht man nicht einmal zu wissen, wonach man sucht.
Daraus folgt eine kurze Liste von Dingen, die dort nicht hingehören:
Keine Passwörter, auch nicht kurz.
Keine Zugangstoken und keine Sitzungsschlüssel.
Keine personenbezogenen Daten, also keine Adressen, keine Geburtsdaten, keine Bestellhistorien.
Wohin dann? Eine Sitzung gehört in ein Cookie mit dem Merkmal HttpOnly, denn an das kommt JavaScript gar nicht erst heran. Echte Daten gehören auf einen Server, wo es Rechte gibt.
Die dritte Grenze: es kann jederzeit weg sein
Der Nutzer kann seine Browserdaten löschen, ohne es zu merken, etwa beim Aufräumen oder weil sein Browser das im privaten Modus von selbst tut.
Deshalb darf nichts in deiner Anwendung davon abhängen, dass ein Eintrag noch da ist. Der Browserspeicher ist eine Bequemlichkeit, kein Datenspeicher. Alles, was verloren gehen darf, kann dort liegen. Alles andere nicht.
Diese Lernplattform hält es selbst so, und du hast die Liste in Lektion 17.1 schon gesehen: Dauerhaft liegt dort genau ein Eintrag von ihr, lern:queue, die Warteschlange der noch nicht gemeldeten Fortschritte. Sie ist der Fall, für den der Browserspeicher gemacht ist, denn sie muss ausgerechnet dann funktionieren, wenn der Server nicht erreichbar ist. Dein Fortschritt selbst steht in der Datenbank, deine Bedienvorlieben hängen am Konto und reisen deshalb zwischen Geräten mit, und dein Code im Editor wird überhaupt nie dauerhaft gespeichert.
Zum Mitnehmen
Alles im Browserspeicher ist für jedes Skript der Seite lesbar. Deshalb gehören dort keine Zugangstoken hinein, niemals.
Jetzt du
Basis Konto, kostenlosZu dieser Lektion gehört eine Aufgabe. Du schreibst den Code selbst, und nach jedem Lauf sagt dir eine Prüfliste, was schon stimmt.
Dafür brauchst du das Basis Konto. Es kostet nichts, und ein Passwort gibt es auch nicht.
Was in dieser Lektion steckt
-
Artikel mit 3 Beispielen zum Ausprobieren
Steht hier, ohne Konto lesbar.
-
Aufgabe im Editor, direkt im Browser geprüft
Öffnet sich mit dem Basis Konto.