Découvrez les bonnes pratiques pour synchroniser l'état de l'application entre IndexedDB et les bibliothèques de gestion d'état populaires.
Lorsqu'un utilisateur charge un site Web ou une application pour la première fois, la construction de l'état initial de l'application utilisé pour afficher l'UI nécessite souvent un travail important. Par exemple, l'application doit parfois authentifier l'utilisateur côté client, puis envoyer plusieurs requêtes API avant de disposer de toutes les données nécessaires à l'affichage sur la page.
Stocker l'état de l'application dans IndexedDB peut être un excellent moyen d'accélérer le temps de chargement lors des visites répétées. L'application peut ensuite se synchroniser avec n'importe quel service d'API en arrière-plan et mettre à jour l'UI avec de nouvelles données de manière différée, en utilisant une stratégie stale-while-revalidate.
Toutefois, lorsque vous utilisez IndexedDB, vous devez tenir compte de nombreux points importants qui ne sont pas forcément évidents pour les développeurs qui découvrent les API. Ce document répond aux questions courantes et aborde certains des points les plus importants à prendre en compte lors de la persistance de l'état de l'application dans IndexedDB.
Préserver la prévisibilité de votre application
Une grande partie des complexités d'IndexedDB provient du fait que de nombreux facteurs échappent à votre contrôle (en tant que développeur). Cette section explore de nombreux problèmes que vous devez garder à l'esprit lorsque vous utilisez IndexedDB.
Les écritures dans l'espace de stockage peuvent échouer
Les erreurs lors de l'écriture dans IndexedDB peuvent se produire pour diverses raisons. Dans certains cas, ces raisons échappent à votre contrôle en tant que développeur. Par exemple, certains navigateurs n'autorisent pas l'écriture dans IndexedDB en mode navigation privée. Il est également possible qu'un utilisateur utilise un appareil dont l'espace disque est presque saturé, et que le navigateur vous empêche de stocker quoi que ce soit.
C'est pourquoi il est essentiel de toujours implémenter une gestion appropriée des erreurs dans votre code IndexedDB. Cela signifie également qu'il est généralement judicieux de conserver l'état de l'application en mémoire (en plus de le stocker), afin que l'UI ne soit pas interrompue lors de l'exécution en mode de navigation privée ou lorsque l'espace de stockage n'est pas disponible (même si certaines des autres fonctionnalités de l'application qui nécessitent un espace de stockage ne fonctionnent pas).
Les données stockées ont peut-être été modifiées ou supprimées par l'utilisateur
Contrairement aux bases de données côté serveur, où vous pouvez restreindre les accès non autorisés, les bases de données côté client sont accessibles aux extensions de navigateur et aux outils pour les développeurs, et peuvent être effacées par l'utilisateur.
Bien qu'il soit rare que les utilisateurs modifient leurs données stockées localement, il est assez courant qu'ils les effacent. Il est important que votre application puisse gérer ces deux cas sans erreur.
Les données stockées sont peut-être obsolètes
Comme dans la section précédente, même si l'utilisateur n'a pas modifié les données lui-même, il est également possible que les données qu'il a stockées aient été écrites par une version antérieure de votre code, éventuellement une version contenant des bugs.
IndexedDB est compatible de manière native avec les versions de schéma et la mise à niveau à l'aide de sa méthode IDBOpenDBRequest.onupgradeneeded(). Toutefois, vous devez toujours écrire votre code de mise à niveau de manière à ce qu'il puisse gérer l'utilisateur provenant d'une version précédente (y compris une version contenant un bug).
Les tests unitaires peuvent être très utiles ici, car il n'est souvent pas possible de tester manuellement tous les chemins et cas de mise à niveau possibles.
Maintenir les performances de votre application
L'une des principales fonctionnalités d'IndexedDB est son API asynchrone, mais ne vous laissez pas tromper en pensant que vous n'avez pas à vous soucier des performances lorsque vous l'utilisez. Dans certains cas, une utilisation incorrecte peut toujours bloquer le thread principal, ce qui peut entraîner un manque de réactivité.
En règle générale, les lectures et écritures dans IndexedDB ne doivent pas être plus volumineuses que nécessaire pour les données auxquelles on accède.
Bien qu'IndexedDB permette de stocker des objets volumineux et imbriqués sous la forme d'un seul enregistrement (ce qui est certes très pratique du point de vue du développeur), cette pratique doit être évitée. En effet, lorsqu'IndexedDB stocke un objet, il doit d'abord créer un clone structuré de cet objet, et le processus de clonage structuré se produit sur le thread principal. Plus l'objet est volumineux, plus le temps de blocage est long.
Cela présente certains défis lors de la planification de la persistance de l'état de l'application dans IndexedDB, car la plupart des bibliothèques de gestion d'état populaires (comme Redux) fonctionnent en gérant l'intégralité de l'arborescence d'état en tant qu'objet JavaScript unique.
Bien que la gestion de l'état de cette manière présente de nombreux avantages et que le stockage de l'intégralité de l'arborescence d'état sous la forme d'un seul enregistrement dans IndexedDB puisse être tentant et pratique, le faire après chaque modification (même si elle est limitée ou retardée) entraînera un blocage inutile du thread principal, augmentera la probabilité d'erreurs d'écriture et, dans certains cas, provoquera même le plantage ou l'absence de réponse de l'onglet du navigateur.
Au lieu de stocker l'intégralité de l'arborescence d'état dans un seul enregistrement, vous devez la diviser en enregistrements individuels et ne mettre à jour que les enregistrements qui changent réellement.
Comme pour la plupart des bonnes pratiques, il ne s'agit pas d'une règle absolue. S'il n'est pas possible de diviser un objet d'état et d'écrire uniquement l'ensemble de modifications minimal, il est préférable de diviser les données en sous-arborescences et de n'écrire que celles-ci, plutôt que d'écrire toujours l'intégralité de l'arborescence d'état. De petites améliorations valent mieux que pas d'amélioration du tout.
Enfin, vous devez toujours mesurer l'impact sur les performances du code que vous écrivez. S'il est vrai que les petites écritures dans IndexedDB sont plus performantes que les écritures volumineuses, cela n'a d'importance que si les écritures dans IndexedDB effectuées par votre application entraînent des tâches longues qui bloquent le thread principal et dégradent l'expérience utilisateur. Il est important de mesurer pour comprendre ce que vous optimisez.
Conclusions
Les développeurs peuvent utiliser des mécanismes de stockage client tels qu'IndexedDB pour améliorer l'expérience utilisateur de leur application, non seulement en conservant l'état d'une session à l'autre, mais aussi en réduisant le temps nécessaire au chargement de l'état initial lors des visites répétées.
Si l'utilisation correcte d'IndexedDB peut considérablement améliorer l'expérience utilisateur, une utilisation incorrecte ou l'absence de gestion des cas d'erreur peut entraîner des applications défectueuses et des utilisateurs mécontents.
Étant donné que le stockage client implique de nombreux facteurs indépendants de votre volonté, il est essentiel que votre code soit bien testé et gère correctement les erreurs, même celles qui peuvent sembler peu probables au départ.