Scopri le best practice per la sincronizzazione dello stato dell'applicazione 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 eseguire il rendering della UI. Ad esempio, a volte l'app deve autenticare l'utente lato client e poi effettuare diverse richieste API prima di avere tutti i dati necessari da visualizzare sulla pagina.
La memorizzazione dello 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'interfaccia utente con i nuovi dati in modo lazy, utilizzando una strategia stale-while-revalidate.
Tuttavia, quando utilizzi IndexedDB, ci sono molti aspetti importanti da considerare che potrebbero non essere immediatamente evidenti agli sviluppatori che non hanno familiarità con le API. Questo documento risponde alle domande più comuni e illustra alcuni degli aspetti più importanti da tenere presente quando si mantiene lo stato dell'applicazione in IndexedDB.
Mantieni la tua app prevedibile
Molte delle complessità di IndexedDB derivano dal fatto che esistono molti fattori su cui tu (lo sviluppatore) non hai alcun controllo. Questa sezione esplora molti dei problemi da tenere presente quando si lavora con IndexedDB.
Le scritture nello spazio di archiviazione potrebbero non riuscire
Gli errori durante la scrittura su IndexedDB possono verificarsi per vari motivi e, in alcuni casi, questi motivi non dipendono da te in qualità di sviluppatore. Ad esempio, alcuni browser non consentono la scrittura su IndexedDB in modalità di navigazione privata. Esiste anche la possibilità che un utente utilizzi un dispositivo con spazio su disco quasi esaurito e che il browser ti impedisca di memorizzare qualsiasi cosa.
Per questo motivo, è fondamentale implementare sempre una gestione degli errori adeguata nel codice IndexedDB. Ciò significa anche che, in genere, è una buona idea mantenere lo stato dell'applicazione in memoria (oltre a memorizzarlo), in modo che l'interfaccia utente non si interrompa 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 lo 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 dati archiviati localmente, è abbastanza comune che li cancellino. È importante che la tua applicazione possa gestire entrambi i casi senza errori.
I dati archiviati potrebbero non essere aggiornati
Come nella sezione precedente, anche se l'utente non ha modificato i dati, è anche possibile che i dati archiviati siano stati scritti da una versione precedente del codice, probabilmente una versione con bug.
IndexedDB dispone di un supporto integrato per le versioni dello schema e l'upgrade utilizzando il metodo IDBOpenDBRequest.onupgradeneeded(). Tuttavia, devi comunque 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 delle unità possono essere molto utili in questo caso, poiché spesso non è possibile testare manualmente tutti i possibili percorsi e casi di upgrade.
Mantieni le prestazioni dell'app
Una delle funzionalità principali di IndexedDB è la sua API asincrona, ma non farti ingannare pensando che non devi preoccuparti delle prestazioni quando la utilizzi. Esistono diversi casi in cui un utilizzo improprio può comunque bloccare il thread principale, il che può causare la mancata risposta.
Come regola generale, le letture e le scritture su IndexedDB non devono essere più grandi di quanto richiesto per i dati a cui si accede.
Sebbene IndexedDB consenta di archiviare oggetti nidificati di grandi dimensioni come un singolo record (e farlo è ammetto che sia piuttosto 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 strutturata avviene nel thread principale. Più grande è l'oggetto, maggiore sarà il tempo di blocco.
Ciò presenta alcune sfide quando si pianifica come rendere persistente lo stato dell'applicazione in IndexedDB, poiché la maggior parte delle librerie di gestione dello stato più diffuse (come Redux) funziona gestendo l'intero 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 un singolo record in IndexedDB possa essere allettante e conveniente, farlo dopo ogni modifica (anche se limitata/con debounce) comporterà un blocco non necessario del thread principale, aumenterà la probabilità di errori di scrittura e, in alcuni casi, causerà persino l'arresto anomalo o la mancata risposta della scheda del browser.
Anziché archiviare l'intero albero di stato in un unico record, devi suddividerlo in singoli record e aggiornare solo quelli che cambiano effettivamente.
Come per la maggior parte delle best practice, non si tratta di una regola del tipo "tutto o niente". Nei casi in cui non è possibile suddividere un oggetto di stato e scrivere solo il set di modifiche minimo, suddividere i dati in sottoalberi e scrivere solo questi è comunque preferibile rispetto a scrivere sempre l'intero albero di stato. Piccoli miglioramenti sono meglio di nessun miglioramento.
Infine, devi sempre misurare l'impatto sulle prestazioni del codice che scrivi. È vero che le scritture di piccole dimensioni su IndexedDB avranno un rendimento migliore rispetto alle scritture di grandi dimensioni, ma questo è importante solo se le scritture su IndexedDB eseguite dalla tua applicazione generano effettivamente 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 lato client come IndexedDB per migliorare l'esperienza utente della loro applicazione non solo mantenendo lo stato tra le sessioni, ma anche riducendo il tempo necessario per caricare lo stato iniziale nelle visite ripetute.
Sebbene l'utilizzo corretto di IndexedDB possa migliorare notevolmente l'esperienza utente, un utilizzo errato o la mancata gestione dei casi di errore possono causare app non funzionanti e utenti insoddisfatti.
Poiché lo spazio di archiviazione del client coinvolge molti fattori al di fuori del tuo controllo, è fondamentale che il codice sia ben testato e gestisca correttamente gli errori, anche quelli che inizialmente potrebbero sembrare improbabili.