Gli oggetti web storage localStorage e sessionStorage permetto di salvare le coppie key/value nel browser. Ciò che è interessante è che i dati rimangono memorizzati anche in seguito al ricaricamento della pagina (per sessionStorage) e anche in seguito a un riavvio del browser (per localStorage). Vedremo come.
Abbiamo già a disposizione i cookies. Perché usare altri oggetti?
- Rispetto ai cookies, gli oggetti web storage non vengono inviati al server con ogni richiesta. Per questo motivo, possiamo archiviarne molti di più. La maggior parte dei browser permette almeno 2 megabytes di dati (o più) e possiedono impostazioni per configurare questa scelta.
- Inoltre, il server non può manipolare la memorizzazione degli oggetti tramite HTTP headers. Tutto viene fatto in JavaScript.
- L’archiviazione è legata alla sorgete (origin) (domain/protocol/port triplet). Questo perché, protocolli differenti o sotto domini deducono diverse archiviazioni a oggetti e non possono accedere ai dati tra di loro.
Le due archiviazioni a oggetti propongono stessi metodi e proprietà:
setItem(key, value): memorizza la coppia key/value.getItem(key): lettura del valore dalla key.removeItem(key): rimuove la key, ed il relativo value.clear(): rimuove tutti gli elementi.key(index): lettura della key all’indiceindex.length: il numero di oggetti archiviati.
Come potete vedere, è simile alla collezione Map (setItem/getItem/removeItem), mantiene comunque l’ordine degli elementi e permette il loro accesso tramite indice con key(index).
Vediamo come funziona.
localStorage demo
Le caratteristiche principali di localStorage sono:
- Condivisione tra le tabs e finestre provenienti dalla stessa origine.
- I dati non scadono. Rimangono in seguito a un riavvio del browser o dell’intero sistema operativo.
Per esempio, il seguente esempio:
localStorage.setItem('test', 1);
…E chiudiamo/apriamo il browser o semplicemente apriamo la stessa pagina in una finestra diversa, possiamo ottenere il risultato atteso in questo modo:
alert( localStorage.getItem('test') ); // 1
Dobbiamo solo essere nello stesso punto di partenza (domain/port/protocol), l’URL di destinazione può essere differente.
Il localStorage è condiviso tra tutte le finestre con la stessa provenienza, quindi se impostiamo i dati in una finestra, il cambiamento diventa visibile anche nelle altre schede.
Accesso in stile oggetto
Possiamo usare la stessa sintassi di lettura/scrittura degli oggetti per accedere agli elementi, in questo modo:
// imposta un nuovo valore
localStorage.test = 2;
// legge il valore
alert( localStorage.test ); // 2
// rimuove il valore
delete localStorage.test;
Questo è permesso per ragioni storiche, e principalmente funziona, ma generalmente non è raccomandato, perché:
-
Se la key è generata dall’utente, può essere qualsiasi cosa, come
lengthotoString, o un altro metodo integrato dilocalStorage. In questo casogetItem/setItemfunziona normalmente, mentre l’accesso in stile oggetto non funziona:let key = 'length'; localStorage[key] = 5; // Error, can't assign length -
C’è un evento
storage, che viene emesso quando modifichiamo dati. Questo evento viene per gli accessi in stile oggetto. Vedremo più avanti nel capitolo.
Cicli sulle keys
Come abbiamo visto, i metodi forniscono funzionalità get/set/remove. Ma come otteniamo tutti i valori o keys salvate?
Sfortunatamente, gli oggetti archiviati non sono iterabili.
Una soluzione sarebbe quella di eseguire un loop su di loro come un array:
for(let i=0; i<localStorage.length; i++) {
let key = localStorage.key(i);
alert(`${key}: ${localStorage.getItem(key)}`);
}
Un altro modo è quello di usare un loop for key in localStorage , come facciamo con oggetti regolari.
L’iterazione avverrà sulle keys, ma anche sugli outputs di campi associati ad alcune funzioni built-in di cui non abbiamo bisogno:
// cattivo esempio
for(let key in localStorage) {
alert(key); // mostra getItem, setItem e altre funzioni integrate
}
… dunque dobbiamo filtrare i campi dal prototype con il controllo hasOwnProperty:
for(let key in localStorage) {
if (!localStorage.hasOwnProperty(key)) {
continue; // salta keys come "setItem", "getItem" etc
}
alert(`${key}: ${localStorage.getItem(key)}`);
}
…oppure possiamo ottenere le keys “proprie” con Object.keys ed eseguire il loop su di loro se necessario:
let keys = Object.keys(localStorage);
for(let key of keys) {
alert(`${key}: ${localStorage.getItem(key)}`);
}
Un lavoro extra, poiché Object.keys restituisce solo le keys che appartengono all oggetto, ignorando il prototype.
Solo stringhe
Da notare che sia key che i value devono essere stringhe.
Se ci fosse un altro tipo di dato, come un numero, o un object, verrebbe automaticamente convertito a stringa:
localStorage.user = {name: "John"};
alert(localStorage.user); // [object Object]
Possiamo usare JSON per archiviare oggetti:
localStorage.user = JSON.stringify({name: "John"});
// successivamente
let user = JSON.parse( localStorage.user );
alert( user.name ); // John
Inoltre è possibile convertire a stringa l’intero archivio di oggetti, per esempio per motivi di debugging:
// aggiunte le opzioni di formattazione a JSON.stringify per rendere object migliore
alert( JSON.stringify(localStorage, null, 2) );
sessionStorage
L’oggetto sessionStorage è usato molto meno spesso del localStorage.
Proprietà e metodi sono gli stessi, ma è più limitato:
- La
sessionStorageesiste solo all’intero della tab del browser corrente.- Un’altra tab con la stessa pagina avrà un archiviazione differente.
- Viene comunque condivisa tra iframes nella stessa tab (assumendo che la loro provenienza sia la stessa).
- I dati sopravvivono al refresh della pagina, ma non alla chiusura/apertura della tab.
Vediamo come si comporta.
Esegui questo codice…
sessionStorage.setItem('test', 1);
…Poi ricarica la pagina. Pra potrai comunque ottenere i dati:
alert( sessionStorage.getItem('test') ); // dopo il refresh: 1
…Ma se apri la stessa pagina in un’altra tab, e provi di nuovo, dal codice otterrai null, ovvero “non è stato trovato nulla”.
Questo perché sessionStorage è legato non solo all’origine, ma anche alla tab del browser. Per questo motivo, sessionStorage è usato sporadicamente.
Evento di storage
Quando i dati vengono aggiornati in localStorage o sessionStorage, un evento di storage viene emesso, con le seguenti proprietà:
key: La key che è stata modificata (nullse è stato invocato.clear()).oldValue: Il vecchio valore (nullse la chiave è nuova).newValue: il nuovo valore (nullse la chiave è sta rimossa).URL: L’URL del documento in cui è avvenuto l’aggiornamento.storageArea: se l’aggiornamento è avvenuto inlocalStorageosessionStorage.
La cosa importante è: l’evento si attiva in tutti gli oggetti window dove l’archivio è accessibile, ad eccezione per quello in cui è stato causato.
Elaboriamo.
Immaginate di avere due finestre aperte con lo stesso sito all’interno. Quindi localStorage è condiviso tra le due.
Dovresti aprire questa pagina in due browser per testare il seguente codice.
Se entrambe le finestre sono connesse a window.onstorage, allora reagiranno agli aggiornamenti che accadono in una delle due.
// attiva un aggiornamento fatto dallo stesso archivio degli altri documenti
window.onstorage = event => { // identico a window.addEventListener('storage', event => {
if (event.key != 'now') return;
alert(event.key + ':' + event.newValue + " at " + event.URL);
};
localStorage.setItem('now', Date.now());
Notare che l’evento contiene: event.URL – l’URL del documento in cui i dati sono stati aggiornati.
Inoltre, event.storageArea contiene lo storage object – l’evento è lo stesso per entrambi sessionStorage e localStorage, quindi event.storageArea si rivolge a quello che è stato modificato. Potremmo anche impostare qualcosa all’interno, per “rispondere” al cambiamento.
That allows different windows from the same origin to exchange messages.
I browser moderni supportano Broadcast channel API, un API speciale per comunicazione inter-finestra provenienti dalla stessa sorgente, possiede molte più funzionalità ma è meno supportata. Esistono librerie che sostituiscono quella API, basate su localStorage, che lo rendono disponibile ovunque.
Riepilogo
Gli oggetti web storage localStorage e sessionStorage permettono di archiviare key/value nel browser.
- Sia
keyevaluedevono essere stringhe. - Il limite è 2mb+, dipende dal browser.
- Non scadono.
- I dati sono legati alla sorgente (domain/port/protocol).
localStorage |
sessionStorage |
|---|---|
| Condivise tra tutte le tabs e finestre provenienti dalla stessa sorgente | Visibile all’interno di una tab del browser, incluso iframes della stessa origine |
| Sopravvivono al riavvio del browser | Sopravvivono al refresh della pagina (ma non alla chiusura della tab) |
API:
setItem(key, value): archivia coppia key/value .getItem(key): legge il valore dalla chiave.removeItem(key): rimuove la chiave con il suo valore.clear(): cancella tutto.key(index): legge il valore all’indiceindex.length: il numero di oggetti archiviati.- Utilizza
Object.keysper ottenere tutte le chiavi. - Accediamo alle keys come proprietà degli oggetti, in questo caso l’evento
storagenon verrà emesso.
Evento di storage:
- Emesso su chiamata di
setItem,removeItem,clear. - Contiene tutti i data riguardo l’operazione (
key/oldValue/newValue), il documentoURLe lo storage objectstorageArea. - Emesso su tutti gli oggetti
windowche hanno accesso all’archivio eccetto quello da cui è stato generato (all’interno di una tab persessionStorage, globalmente perlocalStorage).
Commenti
<code>, per molte righe – includile nel tag<pre>, per più di 10 righe – utilizza una sandbox (plnkr, jsbin, codepen…)