Scopri le best practice per la sincronizzazione dello stato delle applicazioni tra IndexedDB, una popolare libreria di gestione dello stato.
Quando un utente carica per la prima volta un sito web o un'applicazione, spesso è necessaria una discreta quantità di lavoro creando lo stato iniziale dell'applicazione utilizzato per eseguire il rendering dell'interfaccia utente. Ad esempio, a volte deve autenticare il lato client dell'utente ed effettuare varie richieste API prima di poter i dati da visualizzare sulla pagina.
L'archiviazione dello stato dell'applicazione IndexedDB può essere un ottimo modo per velocizzare il tempo di caricamento per le visite ripetute. L'app può quindi sincronizzarsi con qualsiasi servizio API in background e aggiornare la UI con nuovi dati in modo lento, utilizzando strategia stale-what- reconvalida.
IndexedDB può essere utilizzato anche per archiviare contenuti generati dagli utenti, sia come archivi temporanei prima di caricarli sul server o come cache lato client per i dati remoti.
Tuttavia, quando utilizzi IndexedDB, ci sono molti aspetti importanti da considerare che potrebbero non essere per gli sviluppatori che non hanno mai usato le API. In questo articolo troverai risposte a domande comuni e illustra alcuni degli aspetti più importanti da tenere a mente quando si mantengono i dati in IndexedDB.
Mantieni la tua app prevedibile
Molte delle complessità relative a IndexedDB derivano dal fatto che ci sono così tanti fattori che (lo sviluppatore) non ne hanno alcun controllo. Questa sezione esamina molti dei problemi che devi tenere a mente quando lavori con IndexedDB.
Non tutto può essere archiviato in IndexedDB su tutte le piattaforme
Se archivi file di grandi dimensioni generati dagli utenti, come immagini o video, puoi provare a
come oggetti File
o Blob
. Questa operazione funzionerà su alcune piattaforme, ma non su altre. Safari attivo
In particolare, iOS non può archiviare Blob
in IndexedDB.
Fortunatamente non è troppo difficile convertire un Blob
in un ArrayBuffer
e viceversa. Archiviazione
I valori ArrayBuffer
in IndexedDB sono molto supportati.
Ricorda, tuttavia, che un Blob
ha un tipo MIME mentre un ArrayBuffer
non lo ha. Dovrai
memorizzare il tipo insieme al buffer per effettuare la conversione correttamente.
Per convertire un ArrayBuffer
in un Blob
, basta usare il costruttore Blob
.
function arrayBufferToBlob(buffer, type) {
return new Blob([buffer], { type: type });
}
L'altra direzione è leggermente più complessa ed è un processo asincrono. Puoi utilizzare uno dei seguenti
FileReader
per leggere il blob come ArrayBuffer
. Al termine della lettura, un loadend
viene attivato sul lettore. Puoi racchiudere questa procedura in un Promise
in questo modo:
function blobToArrayBuffer(blob) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.addEventListener('loadend', () => {
resolve(reader.result);
});
reader.addEventListener('error', reject);
reader.readAsArrayBuffer(blob);
});
}
La scrittura nello spazio di archiviazione potrebbe non riuscire
Gli errori durante la scrittura in IndexedDB possono verificarsi per diversi motivi e, in alcuni casi, sono al di fuori del tuo controllo in qualità di sviluppatore. Ad esempio, alcuni browser al momento non consentono di scrivere su IndexedDB quando è attiva la modalità di navigazione privata. C'è anche la possibilità che un utente si trovi su un dispositivo che ha quasi esaurito lo spazio su disco. il browser ti impedirà di memorizzare dati.
Per questo motivo, è fondamentale implementare sempre una corretta gestione degli errori nel Codice IndexedDB. Questo significa anche che in genere è consigliabile mantenere in memoria lo stato dell'applicazione oltre alla sua memorizzazione), in modo che l'interfaccia utente non si interrompa durante l'esecuzione in modalità di navigazione privata o quando spazio di archiviazione non è disponibile (anche se alcune delle altre funzionalità dell'app che richiedono spazio di archiviazione non lavoro).
Puoi individuare gli errori nelle operazioni IndexedDB aggiungendo un gestore di eventi per l'evento error
ogni volta che crei un oggetto IDBDatabase
, IDBTransaction
o IDBRequest
.
const request = db.open('example-db', 1);
request.addEventListener('error', (event) => {
console.log('Request error:', request.error);
};
I dati archiviati potrebbero essere stati modificati o eliminati dall'utente
A differenza dei database lato server, in cui puoi limitare l'accesso non autorizzato, i database lato client accessibili alle estensioni del browser e agli strumenti per sviluppatori, che possono essere cancellate dall'utente.
Sebbene sia raro che gli utenti modifichino i propri dati archiviati localmente, è abbastanza comune per gli utenti per cancellarlo. È importante che l'applicazione possa gestire entrambi questi casi senza errori.
I dati archiviati potrebbero non essere aggiornati
Analogamente alla sezione precedente, anche se l'utente non ha modificato i dati stessi, che i dati nello spazio di archiviazione siano stati scritti da una versione precedente del codice, ad esempio una con una versione precedente contenente bug.
IndexedDB offre il supporto integrato per le versioni dello schema e l'upgrade tramite la sua
IDBOpenDBRequest.onupgradeneeded()
; ma devi comunque scrivere il codice di upgrade in modo che possa gestire
derivanti da una versione precedente (inclusa una versione con un bug).
I test delle unità possono essere molto utili in questo caso, poiché spesso non è possibile testare manualmente tutti i possibili percorsi e richieste di upgrade.
Mantenere le prestazioni dell'app
Una delle caratteristiche principali di IndexedDB è la sua API asincrona, ma non lasciarti ingannare pensando che non devi preoccuparti delle prestazioni quando lo usi. Ci sono alcuni casi in cui un uso improprio può comunque bloccare il thread principale, il che può causare jank e mancata risposta.
Come regola generale, le letture e le scritture in IndexedDB non devono essere superiori a quanto richiesto per i dati a cui l'utente accede.
Mentre IndexedDB rende possibile archiviare grandi oggetti nidificati come un singolo record (e questo è è piuttosto pratica dal punto di vista degli sviluppatori), questa pratica dovrebbe essere evitata. La è che quando IndexedDB archivia un oggetto, deve prima creare un clone strutturato di quell'oggetto e il processo di clonazione strutturato avviene sul thread principale. Più grande è il valore più lungo sarà il tempo di blocco.
Ciò presenta alcune sfide quando si pianifica come mantenere lo stato dell'applicazione in IndexedDB, poiché la maggior parte delle popolari librerie di gestione degli stati (come Redux) funzionano gestendo l'intera struttura di stato come singolo oggetto JavaScript.
Sebbene gestire lo stato in questo modo abbia molti vantaggi (ad esempio, semplifica il ragionamento del codice e il debug) e, anche se la semplice memorizzazione dell'intero albero di stati come singolo record in IndexedDB potrebbe essere allettante e conveniente, eseguire questa operazione dopo ogni modifica (anche in caso di limitazione/rimbalzo) comporterà bloccare inutilmente il thread principale, aumenterà la probabilità di errori di scrittura e in alcuni casi la scheda del browser si arresta in modo anomalo o non risponde.
Anziché archiviare l'intero albero di stati in un unico record, suddividelo in singoli record e aggiorna solo quelli che cambiano effettivamente.
Lo stesso vale se archivi elementi di grandi dimensioni come immagini, musica o video in IndexedDB. Memorizza ogni articolo con la propria chiave anziché all'interno di un oggetto più grande, in modo da poter recuperare i dati strutturati senza pagare il costo del recupero del file binario.
Come per la maggior parte delle best practice, questa non è una regola del tutto o niente. Nei casi in cui non sia possibile suddividere un oggetto di stato e scrivere semplicemente il set di modifiche minimo, suddividendo i dati in sottoalberi e solo scrivendoli è preferibile scrivere sempre l'intero albero di stato. Poco è meglio che nessun miglioramento.
Infine, dovresti sempre misurare l'impatto sul rendimento del codice che scrivi. Sebbene sia vero che le scritture di piccole dimensioni su IndexedDB avranno prestazioni migliori di quelle di grandi dimensioni scritture, questo è importante solo se le scritture su IndexedDB che la tua applicazione sta effettivamente eseguendo portano a attività lunghe che bloccano il thread principale e peggiorano l'esperienza utente. È importante effettuare misurazioni in modo da a capire per cosa stai ottimizzando.
Conclusioni
Gli sviluppatori possono sfruttare meccanismi di archiviazione client come IndexedDB per migliorare l'esperienza utente di la loro applicazione non solo rimanendo in uno stato da una sessione all'altra, ma anche diminuendo il tempo per caricare lo stato iniziale in visite ripetute.
L'utilizzo corretto di IndexedDB può migliorare notevolmente l'esperienza utente, oppure utilizzarlo in modo errato o la mancata gestione dei casi di errore può causare app non funzionanti e utenti scontenti.
Poiché lo spazio di archiviazione del client coinvolge molti fattori al di fuori del tuo controllo, è fondamentale che il codice sia ben è testato e gestisce correttamente gli errori, anche quelli che inizialmente potrebbero sembrare improbabili.