Introducción a los diferentes tipos de almacenamiento del navegador

Índice
  1. La localStorageAPI
  2. La API IndexedDB
  3. Galletas
  4. URL storage
  5. Cache API
  6. Bonus: Browser extension
  7. Conclusion

En el desarrollo back-end, el almacenamiento es una parte común del trabajo. Los datos de las aplicaciones se almacenan en bases de datos, los archivos en el almacenamiento de objetos, los datos transitorios en cachés… existen posibilidades aparentemente infinitas para almacenar cualquier tipo de datos. Pero el almacenamiento de datos no se limita sólo al back-end. La interfaz (el navegador) también está equipada con muchas opciones para almacenar datos. Podemos aumentar el rendimiento de nuestra aplicación, guardar las preferencias del usuario, mantener el estado de la aplicación en múltiples sesiones o incluso en diferentes computadoras, utilizando este almacenamiento.

En este artículo veremos las diferentes posibilidades para almacenar datos en el navegador. Cubriremos tres casos de uso de cada método para comprender los pros y los contras. Al final, podrá decidir qué almacenamiento es el mejor para su caso de uso. ¡Así que comencemos!

La localStorageAPI

localStoragees una de las opciones de almacenamiento más populares en el navegador y la opción preferida de muchos desarrolladores. Los datos se almacenan entre sesiones, nunca se comparten con el servidor y están disponibles para todas las páginas bajo el mismo protocolo y dominio. El almacenamiento está limitado a ~5 MB.

Sorprendentemente, el equipo de Google Chrome no recomienda el uso de esta opción, ya que bloquea el hilo principal y no es accesible para los trabajadores web ni para los trabajadores de servicios. Lanzaron un experimento, KV Storage , como una versión mejor, pero fue solo una prueba que no parece haber llegado a ninguna parte todavía.

La localStorageAPI está disponible window.localStoragey solo puede guardar cadenas UTF-16. Debemos asegurarnos de convertir los datos a cadenas antes de guardarlos en localStorage. Las tres funciones principales son:

  • setItem('key', 'value')
  • getItem('key')
  • removeItem('key')

Todos son sincrónicos, lo que hace que sea sencillo trabajar con ellos, pero bloquean el hilo principal.

Cabe mencionar que localStoragetiene un gemelo llamado sessionStorage. La única diferencia es que los datos almacenados sessionStoragedurarán solo para la sesión actual, pero la API es la misma.

Veámoslo en acción. El primer ejemplo demuestra cómo utilizarlo localStoragepara almacenar las preferencias del usuario. En nuestro caso, es una propiedad booleana que activa o desactiva el tema oscuro de nuestro sitio.

Puede marcar la casilla de verificación y actualizar la página para ver que el estado se guarda en todas las sesiones. Eche un vistazo a las funciones savey loadpara ver cómo convierto el valor en cadena y cómo lo analizo. Es importante recordar que solo podemos almacenar cadenas.

Este segundo ejemplo carga nombres de Pokémon de la PokéAPI .

Enviamos una solicitud GET usando fetchy enumeramos todos los nombres en un ulelemento. Al recibir la respuesta, la almacenamos en caché localStoragepara que nuestra próxima visita pueda ser mucho más rápida o incluso funcionar sin conexión. Tenemos que usar JSON.stringifypara convertir los datos a cadena y JSON.parseleerlos del caché.

En este último ejemplo, demuestro un caso de uso en el que el usuario puede navegar a través de diferentes páginas de Pokémon y la página actual se guarda para las próximas visitas.

El problema con localStorage, en este caso, es que el estado se guarda localmente. Este comportamiento no nos permite compartir la página deseada con nuestros amigos. Más adelante veremos cómo superar este problema.

También usaremos estos tres ejemplos en las siguientes opciones de almacenamiento. Bifurqué los Pens y simplemente cambié las funciones relevantes. El esqueleto general es el mismo para todos los métodos.

La API IndexedDB

IndexedDB es una solución de almacenamiento moderna en el navegador. Puede almacenar una cantidad significativa de datos estructurados, incluso archivos y blobs. Como toda base de datos, IndexedDB indexa los datos para ejecutar consultas de manera eficiente. Es más complejo utilizar IndexedDB. Tenemos que crear una base de datos, tablas y usar transacciones.

En comparación con localStorage, IndexedDB requiere mucho más código. En los ejemplos, uso la API nativa con un contenedor Promise , pero recomiendo encarecidamente usar bibliotecas de terceros para ayudarte. Mi recomendación es localForage porque usa la misma localStorageAPI pero la implementa de manera progresiva, lo que significa que si su navegador admite IndexedDB, la usará; y si no, volverá a caer localStorage.

¡Codifiquemos y vayamos a nuestro ejemplo de preferencias de usuario!

idbes el contenedor de Promise que usamos en lugar de trabajar con una API basada en eventos de bajo nivel. Son casi idénticos, así que no te preocupes. Lo primero que hay que notar es que cada acceso a la base de datos es asíncrono, lo que significa que no bloqueamos el hilo principal. En comparación con localStorage, esta es una gran ventaja.

Necesitamos abrir una conexión a nuestra base de datos para que esté disponible en toda la aplicación para lectura y escritura. Le damos a nuestra base de datos un nombre, my-dbuna versión de esquema, 1 y una función de actualización para aplicar cambios entre versiones. Esto es muy similar a las migraciones de bases de datos. Nuestro esquema de base de datos es simple: solo un almacén de objetos, preferences. Un almacén de objetos es el equivalente a una tabla SQL. Para escribir o leer de la base de datos, debemos utilizar transacciones. Esta es la parte tediosa del uso de IndexedDB. Eche un vistazo a las novedades savey loadfunciones en la demostración.

No hay duda de que IndexedDB tiene muchos más gastos generales y la curva de aprendizaje es más pronunciada en comparación con localStorage. Para los casos de valores clave, podría tener más sentido utilizar localStorageuna biblioteca de terceros que nos ayude a ser más productivos.

Los datos de aplicaciones, como en nuestro ejemplo de Pokémon, son el punto fuerte de IndexedDB. Puedes almacenar cientos de megabytes e incluso más en esta base de datos. ¡Puedes almacenar todos los Pokémon en IndexedDB y tenerlos disponibles sin conexión e incluso indexados! Este es definitivamente el que debe elegir para almacenar datos de aplicaciones.

Me salté la implementación del tercer ejemplo, ya que IndexedDB no introduce ninguna diferencia en este caso en comparación con localStorage. Incluso con IndexedDB, el usuario aún no compartirá la página seleccionada con otros ni la marcará como favorita para uso futuro. Ninguno de los dos es adecuado para este caso de uso.

Galletas

El uso de cookies es una opción de almacenamiento única. Es el único almacenamiento que también se comparte con el servidor. Las cookies se envían como parte de cada solicitud. Puede ser cuando el usuario navega por las páginas de nuestra aplicación o cuando el usuario envía solicitudes Ajax. Esto nos permite crear un estado compartido entre el cliente y el servidor, y también compartir el estado entre múltiples aplicaciones en diferentes subdominios. Esto no es posible con otras opciones de almacenamiento que se describen en este artículo. Una advertencia: las cookies se envían con cada solicitud, lo que significa que debemos mantener nuestras cookies pequeñas para mantener un tamaño de solicitud decente.

El uso más común de las cookies es la autenticación, que está fuera del alcance de este artículo. Al igual que las localStoragecookies, las cookies solo pueden almacenar cadenas. Las cookies se concatenan en una cadena separada por punto y coma y se envían en el encabezado de la cookie de la solicitud. Puede establecer muchos atributos para cada cookie, como caducidad, dominios permitidos, páginas permitidas y muchos más.

En los ejemplos, muestro cómo manipular las cookies a través del lado del cliente, pero también es posible cambiarlas en la aplicación del lado del servidor.

Guardar las preferencias del usuario en una cookie puede ser una buena opción si el servidor puede utilizarla de alguna manera. Por ejemplo, en el caso de uso del tema, el servidor puede entregar el archivo CSS relevante y reducir el tamaño potencial del paquete (en caso de que estemos renderizando del lado del servidor). Otro caso de uso podría ser compartir estas preferencias entre múltiples aplicaciones de subdominio sin una base de datos.

Leer y escribir cookies con JavaScript no es tan sencillo como podría pensar. Para guardar una nueva cookie, debe configurarla document.cookie; consulte la savefunción en el ejemplo anterior. Configuro la dark_themecookie y le agrego un max-ageatributo para asegurarme de que no caduque cuando se cierre la pestaña. Además, agrego los atributos SameSitey Secure. Estos son necesarios porque CodePen usa iframe para ejecutar los ejemplos, pero no los necesitará en la mayoría de los casos. Leer una cookie requiere analizar la cadena de la cookie.

Una cadena de cookies tiene este aspecto:

key1=value1;key2=value2;key3=value3

So, first, we have to split the string by semicolon. Now, we have an array of cookies in the form of key1=value1, so we need to find the right element in the array. In the end, we split by the equal sign and get the last element in the new array. A bit tedious, but once you implement the getCookie function (or copy it from my example :P) you can forget it.

Saving application data in a cookie can be a bad idea! It will drastically increase the request size and will reduce application performance. Also, the server cannot benefit from this information as it’s a stale version of the information it already has in its database. If you use cookies, make sure to keep them small.

The pagination example is also not a good fit for cookies, just like localStorage and IndexedDB. The current page is a temporary state that we would like to share with others, and any of these methods do not achieve it.

URL storage

URL is not a storage, per se, but it’s a great way to create a shareable state. In practice, it means adding query parameters to the current URL that can be used to recreate the current state. The best example would be search queries and filters. If we search the term flexbox on CSS-Tricks, the URL will be updated to https://css-tricks.com/?s=flexbox. See how easy it is to share a search query once we use the URL? Another advantage is that you can simply hit the refresh button to get newer results of your query or even bookmark it.

We can save only strings in the URL, and its maximum length is limited, so we don’t have so much space. We will have to keep our state small. No one likes long and intimidating URLs.

Again, CodePen uses iframe to run the examples, so you cannot see the URL actually changing. Worry not, because all the bits and pieces are there so you can use it wherever you want.

We can access the query string through window.location.search and, lucky us, it can be parsed using the URLSearchParams class. No need to apply any complex string parsing anymore. When we want to read the current value, we can use the get function. When we want to write, we can use set. It’s not enough to only set the value; we also need to update the URL. This can be done using history.pushState or history.replaceState, depending on the behavior we want to accomplish.

I wouldn’t recommend saving a user’s preferences in the URL as we will have to add this state to every URL the user visits, and we cannot guarantee it; for example, if the user clicks on a link from Google Search.

Just like cookies, we cannot save application data in the URL as we have minimal space. And even if we did manage to store it, the URL will be long and not inviting to click. Might look like a phishing attack of sorts.

Just like our pagination example, the temporary application state is the best fit for the URL query string. Again, you cannot see the URL changes, but the URL updates with the ?page=x query parameter every time you click on a page. When the web page loads, it looks for this query parameter and fetches the right page accordingly. Now we can share this URL with our friends so they can enjoy our favorite Pokémon.

Cache API

Cache API is a storage for the network level. It is used to cache network requests and their responses. The Cache API fits perfectly with service workers. A service worker can intercept every network request, and using the Cache API, it can easily cache both the requests. The service worker can also return an existing cache item as a network response instead of fetching it from the server. By doing so, you can reduce network load times and make your application work even when offline. Originally, it was created for service workers but in modern browsers the Cache API is available also in window, iframe, and worker contexts as-well. It’s a very powerful API that can improve drastically the application user experience.

Just like IndexedDB the Cache API storage is not limited and you can store hundreds of megabytes and even more if you need to. The API is asynchronous so it will not block your main thread. And it’s accessible through the global property caches.

To read more about the Cache API, the Google Chrome team has made a great tutorial.

Chris created an awesome Pen with a practical example of combining service workers and the Cache API.

Open Demo

Bonus: Browser extension

If you build a browser extension, you have another option to store your data. I discovered it while working on my extension, daily.dev. It’s available via chrome.storage or browser.storage, if you use Mozilla’s polyfill. Make sure to request a storage permission in your manifest to get access.

There are two types of storage options, local and sync. The local storage is self-explanatory; it means it isn’t shared and kept locally. The sync storage is synced as part of the Google account and anywhere you install the extension with the same account this storage will be synced. Pretty cool feature if you ask me. Both have the same API so it’s super easy to switch back-and-forth, if needed. It’s async storage so it doesn’t block the main thread like localStorage. Unfortunately, I cannot create a demo for this storage option as it requires a browser extension but it’s pretty simple to use, and almost like localStorage. For more information about the exact implementation, refer to Chrome docs.

Conclusion

The browser has many options we can utilize to store our data. Following the Chrome team’s advice, our go-to storage should be IndexedDB. It’s async storage with enough space to store anything we want. localStorage is not encouraged, but is easier to use than IndexedDB. Cookies are a great way to share the client state with the server but are mostly used for authentication.

If you want to create pages with a shareable state such as a search page, use the URL’s query string to store this information. Lastly, if you build an extension, make sure to read about chrome.storage.

SUSCRÍBETE A NUESTRO BOLETÍN 
No te pierdas de nuestro contenido ni de ninguna de nuestras guías para que puedas avanzar en los juegos que más te gustan.

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Subir

Este sitio web utiliza cookies para mejorar tu experiencia mientras navegas por él. Este sitio web utiliza cookies para mejorar tu experiencia de usuario. Al continuar navegando, aceptas su uso. Mas informacion