Best practice per mantenere lo stato dell'applicazione con IndexedDB

Scopri le best practice per la sincronizzazione dello stato delle applicazioni tra IndexedDB e le librerie di gestione dello stato più diffuse.

Quando un utente carica per la prima volta un sito web o un'applicazione, spesso è necessario un lavoro considerevole per creare lo stato iniziale dell'applicazione utilizzato per il rendering dell'interfaccia utente. Ad esempio, a volte l'app deve autenticare il lato client dell'utente ed effettuare diverse richieste API prima di avere tutti i dati necessari per visualizzare sulla pagina.

Memorizzare lo stato dell'applicazione in 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 l'UI con nuovi dati in modo lento, utilizzando una strategia inattiva durante la riconvalida.

Tuttavia, quando si utilizza IndexedDB, ci sono molti aspetti importanti da considerare che potrebbero non essere immediatamente ovvi per gli sviluppatori che non hanno mai utilizzato le API. Questo documento risponde alle domande più comuni e illustra alcuni degli aspetti più importanti da tenere presenti quando si esegue la persistenza dello stato dell'applicazione in IndexedDB.

Mantieni l'app prevedibile

Molte delle complessità relative a IndexedDB derivano dal fatto che ci sono così tanti fattori su cui tu (lo sviluppatore) non hai alcun controllo. Questa sezione esplora molti dei problemi che devi tenere a mente quando lavori con IndexedDB.

Le scritture nello spazio di archiviazione potrebbero non riuscire

Gli errori durante la scrittura in IndexedDB possono verificarsi per diversi motivi e, in alcuni casi, esulano dal tuo controllo in qualità di sviluppatore. Ad esempio, alcuni browser non consentono di scrivere su IndexedDB quando è attiva la modalità di navigazione privata. Esiste anche la possibilità che un utente utilizzi un dispositivo che sta per esaurire lo spazio su disco e il browser non consenta di archiviare nulla.

Per questo motivo, è fondamentale implementare sempre una corretta gestione degli errori nel codice IndexedDB. Ciò significa anche che in genere è buona idea mantenere lo stato dell'applicazione in memoria (oltre a memorizzarlo), in modo che l'interfaccia utente non si rompa quando viene eseguita in modalità di navigazione privata o quando lo spazio di archiviazione non è disponibile (anche se alcune delle altre funzionalità dell'app che richiedono spazio di archiviazione non funzioneranno).

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 sono accessibili alle estensioni del browser e agli strumenti per sviluppatori e possono essere cancellati dall'utente.

Sebbene sia raro che gli utenti modifichino i propri dati archiviati localmente, è abbastanza comune che vengano cancellati. È 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 stesso, è possibile che i dati nello spazio di archiviazione siano stati scritti da una versione precedente del codice, probabilmente una versione contenente bug.

IndexedDB supporta le versioni dello schema e l'upgrade utilizzando il metodo IDBOpenDBRequest.onupgradeneeded(). Tuttavia, devi ancora scrivere il codice di upgrade in modo che possa gestire l'utente proveniente da una versione precedente (inclusa una versione con un bug).

I test di unità possono essere molto utili in questo caso, poiché spesso non è possibile testare manualmente tutti i possibili percorsi e casi di upgrade.

Mantenere le prestazioni dell'app

Una delle funzionalità chiave di IndexedDB è la sua API asincrona, ma non farti ingannare dal pensare che non devi preoccuparti del rendimento quando la utilizzi. In alcuni casi, un utilizzo improprio può comunque bloccare il thread principale, causando la mancata reattività.

Come regola generale, le letture e le scritture in IndexedDB non devono essere superiori a quanto necessario per i dati a cui si accede.

Sebbene IndexedDB consenta di archiviare oggetti nidificati e di grandi dimensioni come un singolo record (e questo è certamente molto comodo dal punto di vista dello sviluppatore), questa pratica deve essere evitata. Il motivo è 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 è l'oggetto, più lungo sarà il tempo di blocco.

Ciò presenta alcune sfide quando si pianifica come mantenere stato dell'applicazione in IndexedDB, poiché la maggior parte delle librerie di gestione dello stato più diffuse (come Redux) funziona gestendo l'intera struttura ad albero dello stato come un singolo oggetto JavaScript.

Sebbene la gestione dello stato in questo modo offra molti vantaggi e la memorizzazione dell'intero albero dello stato come singolo record in IndexedDB possa essere allettante e comoda, farlo dopo ogni modifica (anche se limitata/debounced) comporterà un blocco non necessario del thread principale, aumenterà la probabilità di errori di scrittura e, in alcuni casi, causerà persino l'arresto anomalo della scheda del browser o la sua mancata risposta.

Invece di memorizzare l'intero albero dello stato in un singolo record, devi suddividerlo in singoli record e aggiornare solo i record che cambiano effettivamente.

Come per la maggior parte delle best practice, non si tratta di una regola "tutto o niente". Nei casi in cui non è possibile suddividere un oggetto di stato e scrivere semplicemente il set di modifiche minimo, suddividendo i dati in sottoalberi e scrivendoli è comunque preferibile scrivere sempre l'intero albero di stato. I piccoli miglioramenti sono meglio di nessun miglioramento.

Infine, devi sempre misurare l'impatto sulle prestazioni del codice che scrivi. Sebbene sia vero che le scritture di piccole dimensioni su IndexedDB avranno prestazioni migliori di quelle di grandi dimensioni, questo è importante solo se le scritture su IndexedDB che la tua applicazione sta effettivamente portando a attività lunghe che bloccano il thread principale e peggiorano l'esperienza utente. È importante misurare per capire per cosa stai ottimizzando.

Conclusioni

Gli sviluppatori possono utilizzare meccanismi di archiviazione client come IndexedDB per migliorare l'esperienza utente della loro applicazione non solo mantenendo costante lo stato tra le sessioni, ma anche diminuendo il tempo necessario per caricare lo stato iniziale nelle visite ripetute.

Sebbene l'utilizzo corretto di IndexedDB possa migliorare notevolmente l'esperienza utente, l'utilizzo non corretto o la mancata gestione dei casi di errore può portare ad app non funzionanti e utenti insoddisfatti.

Poiché lo spazio di archiviazione del client coinvolge molti fattori non sotto il tuo controllo, è fondamentale che il codice sia ben testato e gestisca correttamente gli errori, anche quelli che inizialmente potrebbero sembrare improbabili.