Todo lo que guardas en variables desaparece al recargar la página. Para que una aplicación recuerde el tema elegido, el carrito o las tareas de la sesión anterior sin montar un servidor, el navegador ofrece un almacén clave-valor por cada web. Es sencillo de usar, pero tiene reglas claras sobre qué guarda, cuánto dura y quién puede leerlo.
1 // localStorage y sessionStorage
Las dos APIs tienen exactamente los mismos métodos. Cambia cuánto tiempo viven los datos:
localStorage.setItem('tema', 'oscuro'); // guardar
localStorage.getItem('tema'); // 'oscuro'
localStorage.getItem('no-existe'); // null
localStorage.removeItem('tema'); // borrar una clave
localStorage.clear(); // borrar todo lo de esta weblocalStorage |
sessionStorage |
|
|---|---|---|
| Duración | Hasta que se borre | Hasta cerrar la pestaña |
| Compartido entre pestañas | Sí, de la misma web | No, cada pestaña el suyo |
| Uso típico | Preferencias, borradores, datos de una app sin servidor | Pasos de un formulario, estado temporal |
Las dos son síncronas: getItem devuelve el valor directamente, sin callbacks ni esperas. Tienen además un límite de tamaño de unos pocos megabytes por web, más que suficiente para preferencias o listas, pero no para ficheros o grandes volúmenes de datos.
2 // Sólo textos: el papel de JSON
La limitación que más errores provoca es que las claves y los valores son siempre textos. Si guardas un número, recuperas un texto; si guardas un objeto, se convierte en '[object Object]' y los datos se pierden.
La solución es JSON (JavaScript Object Notation), un formato de texto que representa objetos, arrays, números, textos, booleanos y null. JavaScript trae dos funciones para pasar de un lado a otro:
const preferencias = { tema: 'oscuro', tamano: 16, avisos: true };
const texto = JSON.stringify(preferencias);
// '{"tema":"oscuro","tamano":16,"avisos":true}'
localStorage.setItem('preferencias', texto);
const leido = JSON.parse(localStorage.getItem('preferencias'));
leido.tamano + 2; // 18: vuelve a ser un númeroJSON no puede representarlo todo. Las funciones y los valores undefined desaparecen, las fechas se convierten en textos y los objetos pierden su clase. Para datos sencillos, como los de esta unidad, no es ningún problema.
JSON.parse lanza un error si el texto no es JSON válido, y la primera vez que el usuario visita la web getItem devuelve null. Lee siempre el almacenamiento dentro de una función que contemple los dos casos y devuelva un valor por defecto.
3 // Quién puede leer los datos
Cada almacenamiento pertenece a un origen: la combinación de protocolo, dominio y puerto. Los datos de https://mi-web.es no son visibles desde https://otra-web.es, ni siquiera desde http://mi-web.es. Durante el desarrollo, localhost con distinto puerto también cuenta como origen distinto, así que una app en el puerto 5173 no ve lo que guardó otra en el 3000.
Dentro del mismo origen, en cambio, cualquier script de la página puede leerlo todo, incluidos los scripts de terceros que cargue la web.
Guarda en el navegador sólo lo que no pasaría nada si leyera otra persona con acceso al dispositivo o un script ajeno: preferencias, borradores, datos de interfaz. Nunca contraseñas ni datos personales sensibles. Y recuerda que el usuario puede borrarlo todo en cualquier momento, así que tu aplicación debe funcionar si no encuentra nada.
Para completar el mapa: las cookies también guardan datos en el navegador, pero su propósito es otro. Se envían automáticamente al servidor con cada petición y se usan sobre todo para mantener sesiones. Y para grandes volúmenes de datos estructurados existe IndexedDB, una base de datos asíncrona integrada en el navegador.
La lista de tareas de las unidades anteriores debe conservarse al recargar. El array tareas es la fuente de verdad y pintar(tareas) ya dibuja la lista.
const CLAVE = 'tareas:v1';
function cargarTareas() {
try {
return JSON.parse(localStorage.getItem(CLAVE)) ?? [];
} catch {
return [];
}
}
function guardarTareas(tareas) {
localStorage.setItem(CLAVE, JSON.stringify(tareas));
}
let tareas = cargarTareas();
pintar(tareas);- Una clave con versión:
tareas:v1permite cambiar el formato en el futuro sin leer datos antiguos incompatibles. - Primera visita:
getItemdevuelvenull,JSON.parse(null)devuelvenully?? []lo sustituye por un array vacío. - Datos corruptos: si alguien editó el valor a mano y no es JSON válido,
JSON.parselanza un error y elcatchdevuelve también un array vacío. - Guardar tras cada cambio: en el listener delegado que marca una tarea como hecha, se añade
guardarTareas(tareas)justo antes depintar(tareas).
4 // Para practicar
Guarda preferencias y datos entre visitas, y comprueba qué ocurre cuando lo guardado no existe o está roto. Cada ejercicio trae el enunciado en los comentarios del código.
Preferencias y contador de visitastraining-daw-sv/modulo-01/06-almacenamiento-en-el-navegador/01_theme_preferenceLista de tareas persistentetraining-daw-sv/modulo-01/06-almacenamiento-en-el-navegador/02_persistent_tasksAutoevaluación
Guardas localStorage.setItem('usuario', { nombre: 'Ana' }) y al leerlo obtienes '[object Object]'. ¿Qué ha pasado?
localStorage sólo guarda textos, así que convirtió el objeto a texto de la forma por defecto. Esa conversión de un objeto cualquiera da '[object Object]' y los datos se pierden. Hay que serializarlo antes con JSON.stringify y reconstruirlo al leer con JSON.parse.Una aplicación guarda las preferencias en sessionStorage. El usuario cierra la pestaña, abre la web de nuevo y las preferencias han desaparecido. ¿Es un fallo?
sessionStorage. Sus datos pertenecen a la pestaña y se borran al cerrarla. Para que sobrevivan entre visitas hay que usar localStorage, que persiste hasta que se borre explícitamente.¿Por qué no es seguro guardar un token de sesión o datos personales sensibles en localStorage?
localStorage. Además, los datos quedan en texto plano en el disco del dispositivo.