Best practice per l'utilizzo di IndexedDB

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.