Comparación de almacenamiento del navegador: localStorage, IndexedDB, Cache API y OPFS

技术架构

Resumen de cinco mecanismos de almacenamiento del navegador

Los navegadores modernos ofrecen 5 mecanismos de almacenamiento principales, cada uno diseñado para diferentes objetivos y casos de uso:

Almacenamiento Tipo de datos Límite de capacidad Persistencia Acceso por hilo
localStorage Clave-valor de cadena ~5-10 MB Permanente Solo hilo principal
sessionStorage Clave-valor de cadena ~5-10 MB Al cerrar pestaña Solo hilo principal
IndexedDB Datos estructurados + Blob Cientos de MB~ilimitado Permanente Hilo principal + Worker
Cache API Pares Request/Response Compartido con cuota de IndexedDB Permanente Hilo principal + Worker
OPFS Sistema de archivos Compartido con cuota de IndexedDB Permanente Solo Worker (síncrono)

localStorage y sessionStorage

Uso básico

localStorage.setItem('theme', 'dark');
const theme = localStorage.getItem('theme');
localStorage.removeItem('theme');
localStorage.clear();

for (let i = 0; i < localStorage.length; i++) {
  const key = localStorage.key(i);
  console.log(key, localStorage.getItem(key));
}

Diferencias clave

Característica localStorage sessionStorage
Duración Permanente Se borra al cerrar pestaña
Alcance Compartido entre pestañas del mismo origen Solo pestaña actual
Uso típico Preferencias de usuario, tokens Borradores de formularios, estado de página

Detección de capacidad

function testLocalStorageQuota() {
  const testKey = '__quota_test__';
  let data = '';
  try {
    for (let i = 0; i < 1024; i++) data += 'a'.repeat(1024);
    for (let mb = 1; mb < 20; mb++) {
      localStorage.setItem(testKey, data.repeat(mb));
    }
  } catch (e) {
    console.log('localStorage quota exceeded');
  } finally {
    localStorage.removeItem(testKey);
  }
}

Advertencias

  • Bloqueo síncrono: La lectura/escritura bloquea el hilo principal; datos grandes afectan el rendimiento
  • Solo cadenas: Los objetos necesitan JSON.stringify/JSON.parse
  • Evento storage: Dispara el evento storage en otras pestañas del mismo origen al modificarse

IndexedDB

Uso básico

const openRequest = indexedDB.open('myDatabase', 1);

openRequest.onupgradeneeded = (event) => {
  const db = event.target.result;
  const store = db.createObjectStore('users', { keyPath: 'id' });
  store.createIndex('name', 'name', { unique: false });
};

openRequest.onsuccess = (event) => {
  const db = event.target.result;
  const tx = db.transaction('users', 'readwrite');
  const store = tx.objectStore('users');

  store.add({ id: 1, name: 'Alice', email: 'alice@example.com' });

  store.get(1).onsuccess = (e) => {
    console.log(e.target.result);
  };
};

Características avanzadas

Característica Descripción
Transacciones Garantiza atomicidad, separación lectura/escritura
Índices Consultas eficientes en campos no clave
Cursores Iterar grandes conjuntos de datos sin cargarlos todos a la vez
Almacenamiento Blob Almacenar objetos File y Blob directamente
Acceso desde Worker Disponible en Service Workers y Web Workers

Simplificado con la biblioteca idb

import { openDB } from 'idb';

const db = await openDB('myDatabase', 1, {
  upgrade(db) {
    db.createObjectStore('users', { keyPath: 'id' });
  }
});

await db.put('users', { id: 1, name: 'Alice' });
const user = await db.get('users', 1);
const allUsers = await db.getAll('users');
await db.delete('users', 1);

Cache API

Uso básico

const cache = await caches.open('v1');

await cache.put('/api/data', new Response(JSON.stringify({ foo: 'bar' })));

const response = await cache.match('/api/data');
const data = await response.json();

await cache.delete('/api/data');

Con Service Worker

self.addEventListener('fetch', (event) => {
  event.respondWith(
    caches.match(event.request).then((cached) => {
      if (cached) return cached;
      return fetch(event.request).then((response) => {
        const clone = response.clone();
        caches.open('v1').then((cache) => {
          cache.put(event.request, clone);
        });
        return response;
      });
    })
  );
});

Comparación de estrategias de caché

Estrategia Descripción Caso de uso
Cache First Caché primero, red si no existe Recursos estáticos, fuentes
Network First Red primero, caché como respaldo Datos de API, contenido actualizado frecuentemente
Stale While Revalidate Devolver caché, actualizar en segundo plano APIs no críticas, imágenes
Cache Only Solo caché Páginas offline, recursos pre-cacheados
Network Only Solo red Solicitudes no GET, datos en tiempo real

OPFS (Origin Private File System)

Uso básico

const root = await navigator.storage.getDirectory();

const fileHandle = await root.getFileHandle('data.bin', { create: true });
const writable = await fileHandle.createWritable();
await writable.write(new Uint8Array([1, 2, 3, 4]));
await writable.close();

const file = await fileHandle.getFile();
const buffer = await file.arrayBuffer();

Acceso síncrono en Workers

// En un Worker — OPFS síncrono (extremadamente rápido)
const root = await navigator.storage.getDirectory();
const fileHandle = await root.getFileHandle('large.bin', { create: true });
const accessHandle = await fileHandle.createSyncAccessHandle();

const buffer = new ArrayBuffer(4);
accessHandle.write(buffer, { at: 0 });
accessHandle.flush();
accessHandle.close();

OPFS vs File System Access API

Característica OPFS File System Access API
Permiso de usuario No requerido El usuario debe seleccionar directorio
Visibilidad Sistema de archivos virtual interno del navegador Sistema de archivos real del SO
Sincronización en Worker Soportado No soportado
Persistencia Permanente Depende del permiso del usuario
Caso de uso Procesamiento de archivos temporales de alto rendimiento Editores de archivos, exportación

Cuota de almacenamiento y persistencia

Consultar cuota disponible

const estimate = await navigator.storage.estimate();
console.log(`Used: ${(estimate.usage / 1024 / 1024).toFixed(2)} MB`);
console.log(`Quota: ${(estimate.quota / 1024 / 1024).toFixed(2)} MB`);

Solicitar persistencia

const persisted = await navigator.storage.persist();
if (persisted) {
  console.log('Storage persisted, will not be auto-evicted');
} else {
  console.log('Storage may be cleared under pressure');
}

Los navegadores pueden eliminar automáticamente el almacenamiento no persistente bajo presión. Llama navigator.storage.persist() para solicitar persistencia.


Guía de decisión

What do you need to store?
├── Simple key-value (< 5MB)
│   ├── Cross-tab sharing → localStorage
│   └── Current tab only → sessionStorage
├── Structured data / many records
│   └── IndexedDB
├── HTTP request/response caching
│   └── Cache API
└── Large files / binary / high-performance I/O
    └── OPFS

Ejemplos del mundo real

Escenario Almacenamiento recomendado Razón
Preferencia de tema del usuario localStorage Clave-valor simple, entre páginas
Borrador de formulario sessionStorage Temporal, limitado a la pestaña
Datos de herramienta offline IndexedDB Estructurado, consultas indexadas
Caché de recursos estáticos Cache API Diseñado para Request/Response
Archivos temporales de procesamiento de video OPFS E/S síncrona en Worker, alto rendimiento

Consideraciones de seguridad

  • Riesgo XSS: localStorage es legible por XSS — nunca almacenes tokens sensibles
  • Cookies HttpOnly: Los tokens de autenticación deben usar cookies HttpOnly + Secure
  • Cifrado de almacenamiento: Cifra datos sensibles con JWT o Hash antes de almacenarlos
  • Aislamiento de mismo origen: Todo el almacenamiento sigue la política de mismo origen; el acceso entre orígenes está bloqueado

Resumen

El almacenamiento del navegador ha evolucionado desde un único localStorage hasta un sistema completo que abarca pares clave-valor, datos estructurados, caché HTTP y sistemas de archivos. La clave para la selección es coincidir con las características de los datos: configuración simple → localStorage, datos estructurados → IndexedDB, caché HTTP → Cache API, E/S de archivos de alto rendimiento → OPFS.

Usa el Analizador de Cookies para inspeccionar la configuración de cookies, la Herramienta JWT para verificar la seguridad de tokens, y la Herramienta Hash para hashear datos sensibles.

Prueba estas herramientas que se ejecutan en tu navegador — no requieren registro →

#localStorage#sessionStorage#IndexedDB#Cache API#存储对比