Scopri le best practice per sincronizzare lo 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 il rendering dell'interfaccia utente. Ad esempio, a volte l'app deve autenticare l'utente lato client ed eseguire diverse richieste API prima di disporre di tutti i dati necessari per la visualizzazione nella 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'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 per gli sviluppatori che non hanno dimestichezza con le API. Questo documento risponde a domande comuni e illustra alcuni degli aspetti più importanti da tenere presente quando si esegue la persistenza dello stato dell'applicazione in IndexedDB.
Mantieni l'app prevedibile
Molte delle complessità di IndexedDB derivano dal fatto che esistono moltissimi fattori su cui tu (lo sviluppatore) non hai alcun controllo. Questa sezione illustra 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, questi motivi non sono sotto il tuo controllo in qualità di sviluppatore. Ad esempio, alcuni browser non consentono di scrivere in IndexedDB in 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 gestione corretta 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 possa essere insolito per gli utenti modificare i dati memorizzati localmente, è abbastanza comune che li cancellino. È importante che la tua applicazione possa gestire entrambi i casi senza errori.
I dati archiviati potrebbero essere obsoleti
Come nella sezione precedente, anche se l'utente non ha modificato i dati, è possibile che i dati archiviati siano stati scritti da una versione precedente del codice, eventualmente con 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 una mancata risposta.
Come regola generale, le letture e le scritture in IndexedDB non devono essere superiori a quelle richieste per i dati a cui si accede.
Sebbene IndexedDB consenta di archiviare oggetti nidificati di grandi dimensioni come un singolo record (e ciò è ampiamente riconosciuto come molto pratico dal punto di vista dello sviluppatore), questa pratica dovrebbe essere evitata. Il motivo è che quando IndexedDB memorizza 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, 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 sia possibile suddividere un oggetto stato e scrivere solo il set di modifiche minimo, è preferibile suddividere i dati in alberi secondari e scrivere solo quelli rispetto a scrivere sempre l'intero albero dello 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 piccole scritture in IndexedDB avranno un rendimento migliore rispetto alle scritture di grandi dimensioni, questo è importante solo se le scritture in 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 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 scorretto o la mancata gestione dei casi di errore può portare ad app inaccessibili e a 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.