Bonnes pratiques d'utilisation de la base de données indexée

Découvrez les bonnes pratiques de synchronisation de l'état d'une application entre IndexedDB et des bibliothèques de gestion d'état populaires.

Lorsqu'un utilisateur charge pour la première fois un site Web ou une application, il y a souvent une part de travail importante à créer l'état initial de l'application utilisé pour afficher l'UI. Par exemple, il arrive que l'application doit authentifier l'utilisateur côté client, puis effectuer plusieurs requêtes API avant d'avoir les données à afficher sur la page.

Stocker l'état de l'application dans IndexedDB peut être un excellent moyen d'accélérer le temps de chargement des visites répétées. L'application peut alors se synchroniser avec n'importe quel service d'API en arrière-plan. et mettre à jour l'UI avec les nouvelles données, en utilisant stale-while-revalidate.

IndexedDB permet également de stocker du contenu généré par l'utilisateur, soit en tant que stockage temporaire avant qu'elles ne soient téléchargées sur le serveur ou en tant que cache de données distantes côté client, ou bien les deux.

Toutefois, lorsque vous utilisez IndexedDB, vous devez prendre en compte de nombreux points importants aux développeurs qui ne connaissent pas les API. Cet article répond aux questions fréquentes présente certaines des choses les plus importantes à garder à l'esprit lors de la persistance de données dans IndexedDB.

Préserver la prévisibilité de votre application

Les complexités liées à IndexedDB proviennent en grande partie du fait qu'il existe de nombreux facteurs (le développeur) n'ont aucun contrôle sur ce point. Cette section aborde la plupart des problèmes que vous devez garder à l'esprit. lorsque vous travaillez avec IndexedDB.

Toutes les plates-formes ne peuvent pas être stockées dans IndexedDB

Si vous stockez des fichiers volumineux générés par les utilisateurs, tels que des images ou des vidéos, vous pouvez essayer de stocker comme des objets File ou Blob. Cela fonctionnera sur certaines plates-formes, mais échouera sur d'autres. Safari activé iOS, en particulier, ne peut pas stocker de Blob dans IndexedDB.

Heureusement, il n'est pas trop difficile de convertir un Blob en ArrayBuffer, et inversement. Stockage Les ArrayBuffers dans IndexedDB sont très bien pris en charge.

Cependant, n'oubliez pas qu'un Blob a un type MIME, contrairement à un ArrayBuffer. Vous devrez stocker le type avec le tampon pour effectuer correctement la conversion.

Pour convertir un ArrayBuffer en Blob, il vous suffit d'utiliser le constructeur Blob.

function arrayBufferToBlob(buffer, type) {
  return new Blob([buffer], { type: type });
}

L'autre direction est légèrement plus complexe et est un processus asynchrone. Vous pouvez utiliser un FileReader pour lire le blob en tant que ArrayBuffer. Lorsque la lecture est terminée, un loadend est déclenché sur le lecteur. Vous pouvez encapsuler ce processus dans un Promise comme suit:

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);
  });
}

Échec de l'écriture dans l'espace de stockage

Des erreurs d'écriture dans IndexedDB peuvent se produire pour diverses raisons. Dans certains cas, échappent à votre contrôle en tant que développeur. Par exemple, certains navigateurs n'autorisent pas écriture dans IndexedDB en mode navigation privée Il est également possible qu'un utilisateur utilise un appareil qui manque presque d'espace disque et que vous empêchera de stocker quoi que ce soit.

C'est pourquoi il est essentiel de toujours mettre en œuvre une gestion des erreurs Code IndexedDB. Cela signifie également qu'il est généralement recommandé de conserver l'état de l'application en mémoire (dans en plus de la stocker), de sorte que l'interface utilisateur reste accessible en mode navigation privée est indisponible (même si certaines des autres fonctionnalités de l'application qui nécessitent de l'espace de stockage travail).

Vous pouvez détecter les erreurs dans les opérations IndexedDB en ajoutant un gestionnaire d'événements pour l'événement error. chaque fois que vous créez un objet IDBDatabase, IDBTransaction ou IDBRequest.

const request = db.open('example-db', 1);
request.addEventListener('error', (event) => {
  console.log('Request error:', request.error);
};

Les données stockées peuvent avoir é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 accessibles aux extensions du navigateur et aux outils de développement, et peuvent être effacées par l'utilisateur.

Même s'il est rare que les utilisateurs modifient leurs données stockées localement, cette pratique est fréquente pour l'effacer. 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 s'agit également il est possible que les données stockées aient été écrites par une ancienne version de votre code, contenant des bugs.

IndexedDB est compatible avec les versions de schéma et la mise à niveau via son IDBOpenDBRequest.onupgradeneeded() . Toutefois, vous devez quand même écrire votre code de mise à niveau de sorte qu'il puisse gérer les utilisateurs provenant d'une version précédente (y compris une version avec un bug).

Les tests unitaires peuvent s'avérer très utiles dans ce cas, car il n'est souvent pas possible de tester manuellement tous les les chemins de mise à niveau et les cas d'utilisation.

Préserver 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 inquiéter des performances lorsque vous l'utilisez. Il existe un certain nombre de cas où une utilisation incorrecte peut toujours bloquer le thread principal, ce qui peut entraîner des à-coups et un manque de réactivité.

En règle générale, les lectures et les écritures dans IndexedDB ne doivent pas dépasser la limite requise pour les données en cours d'accès.

La base de données "IndexedDB" permet de stocker des objets volumineux et imbriqués sous la forme d'un enregistrement unique certes très pratique du point de vue du développeur), cette pratique doit être évitée. La est que, lorsque IndexedDB stocke un objet, il doit d'abord créer clone structuré de cet objet, et le processus de clonage structuré s'effectue sur le thread principal. Plus la plus le temps de blocage est long.

Cela présente des difficultés 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 (telles que Redux) fonctionnent en gérant l'arborescence d'état complète sous la forme d'un objet JavaScript unique.

Bien que la gestion de l'état présente de nombreux avantages (par exemple, elle facilite le raisonnement de votre code et débogage). Si vous stockez simplement l'intégralité de l'arborescence d'état sous la forme d'un seul enregistrement dans IndexedDB, tentant et pratique d'effectuer cette opération après chaque modification (même en cas de limitation/rejeu) bloquer inutilement le thread principal, cela augmente la probabilité d'erreurs d'écriture. dans certains cas, il proviendra même que l’onglet du navigateur plante ou ne répond plus.

Au lieu de stocker l'intégralité de l'arborescence d'état dans un seul enregistrement, vous devez la diviser en les enregistrements et ne mettent à jour que ceux qui changent réellement.

Il en va de même si vous stockez des éléments volumineux tels que des images, de la musique ou des vidéos dans IndexedDB. Stocker chaque article avec sa propre clé plutôt que dans un objet plus grand, ce qui vous permet de récupérer les données structurées sans devoir payer les frais liés à la récupération du fichier binaire.

Comme pour la plupart des bonnes pratiques, il ne s'agit pas d'une règle du tout ou rien. Dans les cas où il n'est pas possible diviser un objet d'état et écrire l'ensemble de modifications minimal, en divisant les données en sous-arborescences Il est toujours préférable d'écrire uniquement l'arborescence d'état complète. Peu s'améliorent plutôt que pas 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 seront plus performantes que les grandes les écritures, cela n'a d'importance que si les écritures sur IndexedDB effectuées par votre application conduisent de longues tâches qui bloquent le thread principal et dégradent l'expérience utilisateur. Il est important de mesurer afin de à comprendre l'objectif de l'optimisation.

Conclusions

Les développeurs peuvent utiliser les mécanismes de stockage client comme IndexedDB pour améliorer l'expérience utilisateur leur application non seulement en conservant l'état d'une session à l'autre, mais aussi en réduisant le temps nécessaire pour charger l'état initial lors de visites répétées.

Bien qu'une utilisation appropriée de IndexedDB puisse améliorer considérablement l'expérience utilisateur, une utilisation incorrecte ou ne pas gérer les 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 que vous ne contrôlez pas, il est essentiel que votre code testé et gère correctement les erreurs, même celles qui peuvent sembler peu probables au départ.