Los proveedores de navegadores y los expertos en rendimiento web han estado diciendo durante la mayor parte de la última década que localStorage
es lento y que los desarrolladores web deberían dejar de usarlo.
Para ser justos, las personas que dicen esto no están equivocadas. localStorage
es una API síncrona que bloquea el subproceso principal y, cada vez que accedes a ella, es posible que evites que tu página sea interactiva.
El problema es que la API de localStorage
es muy tentadoramente simple, y la única alternativa asíncrona a localStorage
es IndexedDB, que (seamos sinceros) no se conoce por su facilidad de uso ni por su API amigable.
Por lo tanto, los desarrolladores tienen la opción de elegir entre algo difícil de usar y algo que es malo para el rendimiento. Si bien hay bibliotecas que ofrecen la simplicidad de la API de localStorage
y, al mismo tiempo, usan APIs de almacenamiento asíncrono en segundo plano, incluir una de esas bibliotecas en tu app tiene un costo de tamaño de archivo y puede afectar tu presupuesto de rendimiento.
Pero ¿qué pasaría si fuera posible obtener el rendimiento de una API de almacenamiento asíncrono con la simplicidad de la API de localStorage
, sin tener que pagar el costo del tamaño del archivo?
Bueno, es posible que pronto haya una. Chrome está experimentando con una nueva función conocida como módulos integrados, y el primero que planeamos lanzar es un módulo de almacenamiento de pares clave-valor asíncrono llamado KV Storage.
Pero antes de entrar en los detalles del módulo de almacenamiento de KV, permíteme explicar qué quiero decir con módulos integrados.
¿Qué son los módulos integrados?
Los módulos integrados son como los módulos normales de JavaScript, excepto que no se deben descargar porque se envían con el navegador.
Al igual que las APIs web tradicionales, los módulos integrados deben pasar por un proceso de estandarización. Cada uno tendrá su propia especificación que requerirá una revisión de diseño y signos positivos de compatibilidad de los desarrolladores web y otros proveedores de navegadores antes de que se pueda enviar. (En Chrome, los módulos integrados seguirán el mismo proceso de inicio que usamos para implementar y enviar todas las APIs nuevas).
A diferencia de las APIs web tradicionales, los módulos integrados no se exponen en el alcance global, solo están disponibles a través de importaciones.
No exponer módulos integrados de forma global tiene muchas ventajas: no agregarán ninguna sobrecarga para iniciar un nuevo contexto de tiempo de ejecución de JavaScript (p.ej., una pestaña, un trabajador o un trabajador de servicio nuevos) y no consumirán memoria ni CPU, a menos que se importen. Además, no corren el riesgo de generar colisiones de nombres con otras variables definidas en tu código.
Para importar un módulo integrado, usa el prefijo std:
seguido del identificador del módulo integrado. Por ejemplo, en los navegadores compatibles, puedes importar el módulo de almacenamiento de KV con el siguiente código (consulta a continuación cómo usar un polyfill de almacenamiento de KV en navegadores no compatibles):
import storage, {StorageArea} from 'std:kv-storage';
El módulo de almacenamiento de KV
El módulo de almacenamiento en KV es similar en su simplicidad a la API de localStorage
, pero su forma de API es más cercana a una Map
de JavaScript.
En lugar de getItem()
, setItem()
y removeItem()
, tiene get()
, set()
y delete()
.
También tiene otros métodos similares a los mapas que no están disponibles para localStorage
, como keys()
, values()
y entries()
, y al igual que Map
, sus claves no tienen que ser cadenas. Pueden ser cualquier tipo serializable estructurado.
A diferencia de Map
, todos los métodos de KV Storage muestran promesas o iteradores asíncronos (ya que el punto principal de este módulo es que no es síncrono, a diferencia de localStorage
). Para ver la API completa en detalle, puedes consultar la especificación.
Como puedes ver en el ejemplo de código anterior, el módulo de almacenamiento de KV tiene una exportación predeterminada storage
y una exportación con nombre StorageArea
.
storage
es una instancia de la clase StorageArea
con el nombre 'default'
, y es lo que los desarrolladores usarán con mayor frecuencia en el código de su aplicación. La clase StorageArea
se proporciona para los casos en los que se necesita aislamiento adicional (p.ej., una biblioteca de terceros que almacena datos y desea evitar conflictos con los datos almacenados a través de la instancia predeterminada de storage
). Los datos de StorageArea
se almacenan en una base de datos de IndexedDB con el nombre kv-storage:${name}
, donde el nombre es el nombre de la instancia de StorageArea
.
Este es un ejemplo de cómo usar el módulo de almacenamiento de KV en tu código:
import storage from 'std:kv-storage';
const main = async () => {
const oldPreferences = await storage.get('preferences');
document.querySelector('form').addEventListener('submit', async () => {
const newPreferences = Object.assign({}, oldPreferences, {
// Updated preferences go here...
});
await storage.set('preferences', newPreferences);
});
};
main();
¿Qué sucede si un navegador no admite un módulo integrado?
Si conoces el uso de módulos nativos de JavaScript en navegadores, es probable que sepas que (al menos hasta ahora) importar cualquier cosa que no sea una URL generará un error. Además, std:kv-storage
no es una URL válida.
Por lo tanto, surge la pregunta: ¿tenemos que esperar hasta que todos los navegadores admitan módulos integrados para poder usarlos en nuestro código? Por suerte, la respuesta es no.
En realidad, puedes usar módulos integrados en cuanto un navegador los admita gracias a la ayuda de otra función con la que estamos experimentando llamada import maps.
Importa mapas
Los mapas de importación son, en esencia, un mecanismo con el que los desarrolladores pueden asignar alias a los identificadores de importación a uno o más identificadores alternativos.
Esto es potente porque te brinda una forma de cambiar (en el tiempo de ejecución) la forma en que un navegador resuelve un identificador de importación en particular en toda tu aplicación.
En el caso de los módulos integrados, esto te permite hacer referencia a un polyfill del módulo en el código de tu aplicación, pero un navegador que admita el módulo integrado puede cargar esa versión.
A continuación, te mostramos cómo declarar un mapa de importación para que funcione con el módulo de almacenamiento de KV:
<!-- The import map is inlined into your page -->
<script type="importmap">
{
"imports": {
"/path/to/kv-storage-polyfill.mjs": [
"std:kv-storage",
"/path/to/kv-storage-polyfill.mjs"
]
}
}
</script>
<!-- Then any module scripts with import statements use the above map -->
<script type="module">
import storage from '/path/to/kv-storage-polyfill.mjs';
// Use `storage` ...
</script>
El punto clave del código anterior es que la URL /path/to/kv-storage-polyfill.mjs
se asigna a dos recursos diferentes: std:kv-storage
y, luego, la URL original, /path/to/kv-storage-polyfill.mjs
.
Por lo tanto, cuando el navegador encuentra una sentencia de importación que hace referencia a esa URL (/path/to/kv-storage-polyfill.mjs
), primero intenta cargar std:kv-storage
y, si no puede, vuelve a cargar /path/to/kv-storage-polyfill.mjs
.
Una vez más, la magia aquí es que el navegador no necesita admitir mapas de importación ni módulos integrados para que esta técnica funcione, ya que la URL que se pasa a la sentencia de importación es la URL del polyfill. El polyfill no es un reemplazo, es el predeterminado. El módulo integrado es una mejora progresiva.
¿Qué sucede con los navegadores que no admiten módulos?
Para usar mapas de importación para cargar módulos integrados de forma condicional, debes usar sentencias import
, lo que también significa que debes usar secuencias de comandos de módulo, es decir, <script type="module">
.
Actualmente, más del 80% de los navegadores admiten módulos. En el caso de los que no lo hacen, puedes usar la técnica module/nomodule para entregar un paquete heredado. Ten en cuenta que, cuando generes tu compilación de nomodule
, deberás incluir todos los polyfills, ya que sabes con certeza que los navegadores que no admiten módulos definitivamente no admitirán módulos integrados.
Demostración de KV Storage
Para ilustrar que es posible usar módulos integrados y, al mismo tiempo, admitir navegadores más antiguos, creé una demo que incorpora todas las técnicas descritas anteriormente y se ejecuta en todos los navegadores actuales:
- Los navegadores que admiten módulos, mapas de importación y el módulo integrado no cargan código innecesario.
- Los navegadores que admiten módulos y mapas de importación, pero no el módulo integrado, cargan el polyfill de almacenamiento de KV (a través del cargador de módulos del navegador).
- Los navegadores que admiten módulos, pero no admiten mapas de importación, también cargan la polyfill de almacenamiento de KV (a través del cargador de módulos del navegador).
- Los navegadores que no admiten módulos obtienen el polyfill de almacenamiento de KV en su paquete heredado (cargado a través de
<script nomodule>
).
La demostración se aloja en Glitch, por lo que puedes ver su fuente. También tengo una explicación detallada de la implementación en el archivo README. Si quieres saber cómo se creó, no dudes en echarle un vistazo.
Para ver el módulo integrado nativo en acción, debes cargar la demostración en Chrome 74 o versiones posteriores con la marca experimental de funciones de la plataforma web activada (chrome://flags/#enable-experimental-web-platform-features
).
Puedes verificar que se está cargando el módulo integrado porque no verás la secuencia de comandos de polyfill en el panel de origen de DevTools. En su lugar, verás la versión del módulo integrado (dato curioso: puedes inspeccionar el código fuente del módulo o incluso colocar puntos de interrupción en él):
Envíanos tus comentarios
Esta introducción debería haberte dado una idea de lo que es posible hacer con los módulos integrados. Y esperamos que estés entusiasmado. Nos encantaría que los desarrolladores probaran el módulo de almacenamiento de KV (así como todas las funciones nuevas que se analizaron aquí) y nos enviaran sus comentarios.
Estos son los vínculos de GitHub en los que puedes enviarnos comentarios sobre cada una de las funciones que se mencionan en este artículo:
Si tu sitio usa localStorage
, deberías intentar cambiar a la API de KV Storage para ver si satisface todas tus necesidades. Además, si te registras en la prueba de origen de almacenamiento de KV, puedes implementar estas funciones hoy mismo. Todos tus usuarios deberían beneficiarse de un mejor rendimiento de almacenamiento, y los usuarios de Chrome 74 y versiones posteriores no tendrán que pagar ningún costo adicional de descarga.