Prácticas recomendadas para usar IndexedDB

Conoce las prácticas recomendadas para sincronizar el estado de la aplicación entre IndexedDB, una popular biblioteca de administración de estados.

Cuando un usuario carga por primera vez un sitio web o una aplicación, suele implicar bastante trabajo construir el estado inicial de la aplicación que se usa para renderizar la IU. Por ejemplo, a veces el app necesita autenticar al usuario del lado del cliente y realizar varias solicitudes a la API antes de que los datos que debe mostrar en la página.

Almacenar el estado de la aplicación en IndexedDB puede ser una excelente manera de acelerar el tiempo de carga de las visitas repetidas. Luego, la app puede sincronizarse con cualquier servicio de la API en segundo plano. y actualizar la IU con datos nuevos de forma diferida, stale-while-revalidate.

Otro buen uso de IndexedDB es almacenar contenido generado por usuarios, ya sea como almacenamiento temporal. antes de cargarlos en el servidor o como una caché del cliente de datos remotos o, por supuesto, ambos.

Sin embargo, cuando se usa IndexedDB hay muchos aspectos importantes a tener en cuenta que pueden no ser que es inmediatamente obvio para los desarrolladores que no tienen experiencia con las APIs. En este artículo, se responden preguntas comunes y se analizan algunos de los aspectos más importantes que se deben tener en cuenta a la hora de conservar datos en IndexedDB.

Haz que tu app sea predecible

Muchas de las complejidades de IndexedDB surgen del hecho de que hay tantos factores (el desarrollador) no tienen control sobre ellas. En esta sección, se exploran muchos de los problemas que debes tener en cuenta cuando trabajas con IndexedDB.

No todo se puede almacenar en IndexedDB en todas las plataformas.

Si almacenas archivos grandes generados por el usuario, como imágenes o videos, puedes tratar de almacenar como objetos File o Blob. Esto funcionará en algunas plataformas, pero fallará en otras. Safari activado iOS, en particular, no puede almacenar objetos Blob en IndexedDB.

Afortunadamente, no es muy difícil convertir Blob en ArrayBuffer, y viceversa. Almacenamiento ArrayBuffer en IndexedDB es muy compatible.

Sin embargo, recuerda que un Blob tiene un tipo de MIME, mientras que un ArrayBuffer no. Deberás almacenar el tipo junto con el búfer para realizar la conversión correctamente.

Para convertir un ArrayBuffer en un Blob, simplemente usa el constructor Blob.

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

La otra dirección es un poco más compleja y es un proceso asíncrono. Puedes usar un FileReader para leer el BLOB como un ArrayBuffer. Cuando la lectura finalice, una loadend evento se activa en el lector. Puedes unir este proceso en un Promise de la siguiente manera:

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

Es posible que falle la escritura en el almacenamiento

Los errores cuando se escribe en IndexedDB pueden ocurrir por varias razones y, en algunos casos, que están fuera de tu control como desarrollador. Por ejemplo, algunos navegadores actualmente no permiten Cómo escribir en IndexedDB en el modo de navegación privada. También existe la posibilidad de que un usuario esté en un dispositivo que casi no tiene espacio en el disco. navegador impedirá que guardes ningún elemento.

Por eso, es muy importante que siempre implementes un manejo adecuado de errores Código IndexedDB. Esto también significa que, en general, es buena idea mantener el estado de la aplicación en la memoria (en además de almacenarla), para que la IU no falle cuando se ejecute en el modo de navegación privada o cuando no hay espacio de almacenamiento disponible (incluso si algunas de las funciones de la app que requieren el trabajo).

Puedes detectar errores en las operaciones de IndexedDB si agregas un controlador para el evento error cada vez que crees un objeto IDBDatabase, IDBTransaction o IDBRequest.

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

Es posible que el usuario haya modificado o borrado los datos almacenados

A diferencia de las bases de datos del servidor, donde puedes restringir el acceso no autorizado, las extensiones del navegador y las herramientas para desarrolladores, y el usuario puede autorizarlas.

Si bien puede ser poco común que los usuarios modifiquen sus datos almacenados localmente, es bastante común para borrarlo. Es importante que tu aplicación pueda manejar ambos casos sin errores.

Es posible que los datos almacenados estén desactualizados

Al igual que en la sección anterior, incluso si el usuario no ha modificado los datos, también es de que los datos que tienen almacenados se hayan escrito con una versión anterior de tu código, versión con errores.

IndexedDB tiene compatibilidad integrada con versiones de esquema y actualizaciones a través de su IDBOpenDBRequest.onupgradeneeded() método; Sin embargo, aún debe escribir su código de actualización de manera que pueda controlar el usuario provenientes de una versión anterior (incluida una con un error).

Las pruebas de unidades pueden ser muy útiles aquí, ya que a menudo no es posible probar manualmente todos los las rutas de actualización y los casos.

Cómo mantener el rendimiento de tu app

Una de las características clave de IndexedDB es su API asíncrona, pero no dejes que eso te engañe. y crees que no tienes que preocuparte por el rendimiento cuando lo usas. Hay diversos casos en los que El uso inadecuado puede bloquear el subproceso principal, lo que puede provocar bloqueos y faltas de respuesta.

Como regla general, las operaciones de lectura y escritura en IndexedDB no deben ser más grandes de lo necesario para los datos. a las que se está accediendo.

Si bien IndexedDB permite almacenar objetos grandes y anidados como un solo registro (y hacerlo no es desde la perspectiva de un desarrollador), se debe evitar esta práctica. El Esto se debe a que, cuando IndexedDB almacena un objeto, primero debe crear clonación estructurada el objeto, y el proceso de clonación estructurada tiene lugar en el subproceso principal. Cuanto más grande sea mayor será el tiempo de bloqueo.

Esto presenta algunos desafíos a la hora de planificar cómo conservar el estado de la aplicación en IndexedDB, ya que la mayoría algunas de las bibliotecas de administración de estados populares (como Redux) administran todo el árbol de estados como un único objeto de JavaScript.

Si bien administrar el estado de esta manera tiene muchos beneficios (p.ej., hace que su código sea fácil de razonar y depurar), y si bien solo almacenar todo el árbol de estados como un único registro en IndexedDB, tentador y conveniente, hacer esto después de cada cambio (aunque se limite o anule) bloqueo innecesario del subproceso principal, aumentará la probabilidad de errores de escritura y, en en algunos casos, incluso hará que la pestaña del navegador falle o deje de responder.

En lugar de almacenar todo el árbol de estados en un solo registro, debes dividirlo en y solo actualizará los que realmente cambien.

Lo mismo sucede si almacenas elementos grandes, como imágenes, música o videos, en IndexedDB. Almacena cada artículo con su propia clave, en lugar de dentro de un objeto más grande, de modo que puedas recuperar los datos estructurados sin pagar el costo de recuperar también el archivo binario.

Como en la mayoría de las prácticas recomendadas, esta no es una regla de todo o nada. En los casos en que no sea factible dividir un objeto de estado, escribir el conjunto de cambios mínimo y dividir los datos en subárboles y solo se prefiere escribirlos que escribir todo el árbol de estados. Poca las mejoras son mejores que ninguna en absoluto.

Por último, siempre debes medir el impacto en el rendimiento del código que escribas. Si bien es cierto que las escrituras pequeñas en IndexedDB tendrán un mejor rendimiento que las grandes de escritura, esto solo importa si las escrituras en IndexedDB que realiza tu aplicación realmente generan tareas largas que bloquean el subproceso principal y degradan la experiencia del usuario. Es importante medir para a comprender para qué las optimizas.

Conclusiones

Los desarrolladores pueden aprovechar los mecanismos de almacenamiento del cliente, como IndexedDB, para mejorar la experiencia del usuario de su aplicación no solo porque se conserva el estado en todas las sesiones, sino también al disminuir el tiempo que tarda en cargar el estado inicial en visitas repetidas.

Si bien el uso correcto de IndexedDB puede mejorar drásticamente la experiencia del usuario, su uso incorrecto o no manejar casos de error puede dar lugar a apps dañadas y a usuarios insatisfechos.

Como el almacenamiento del cliente involucra muchos factores fuera de tu control, es fundamental que tu código y maneja los errores de forma adecuada, incluso aquellos que al principio parecen improbables.