Visão geral dos cinco mecanismos de armazenamento do navegador
Os navegadores modernos oferecem 5 mecanismos de armazenamento principais, cada um projetado para diferentes objetivos e casos de uso:
| Armazenamento |
Tipo de dados |
Limite de capacidade |
Persistência |
Acesso por thread |
| localStorage |
Chave-valor de string |
~5-10 MB |
Permanente |
Apenas thread principal |
| sessionStorage |
Chave-valor de string |
~5-10 MB |
Ao fechar aba |
Apenas thread principal |
| IndexedDB |
Dados estruturados + Blob |
Centenas de MB~ilimitado |
Permanente |
Thread principal + Worker |
| Cache API |
Pares Request/Response |
Compartilhado com cota do IndexedDB |
Permanente |
Thread principal + Worker |
| OPFS |
Sistema de arquivos |
Compartilhado com cota do IndexedDB |
Permanente |
Apenas Worker (síncrono) |
localStorage e 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));
}
Diferenças principais
| Característica |
localStorage |
sessionStorage |
| Duração |
Permanente |
Limpo ao fechar aba |
| Escopo |
Compartilhado entre abas de mesma origem |
Apenas aba atual |
| Uso típico |
Preferências do usuário, tokens |
Rascunhos de formulário, estado da página |
Detecção de capacidade
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);
}
}
Ressalvas
- Bloqueio síncrono: Leitura/escrita bloqueia a thread principal; dados grandes prejudicam o desempenho
- Apenas strings: Objetos precisam de
JSON.stringify/JSON.parse
- Evento storage: Dispara o evento
storage em outras abas de mesma origem ao ser modificado
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);
};
};
Recursos avançados
| Recurso |
Descrição |
| Transações |
Garante atomicidade, separação leitura/escrita |
| Índices |
Consultas eficientes em campos não-chave |
| Cursores |
Iterar grandes conjuntos de dados sem carregar tudo de uma vez |
| Armazenamento Blob |
Armazenar objetos File e Blob diretamente |
| Acesso por Worker |
Disponível em Service Workers e Web Workers |
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');
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;
});
})
);
});
Comparação de estratégias de cache
| Estratégia |
Descrição |
Caso de uso |
| Cache First |
Cache primeiro, rede se não encontrado |
Recursos estáticos, fontes |
| Network First |
Rede primeiro, cache como fallback |
Dados de API, conteúdo atualizado frequentemente |
| Stale While Revalidate |
Retornar cache, atualizar em segundo plano |
APIs não críticas, imagens |
| Cache Only |
Apenas cache |
Páginas offline, recursos pré-cacheados |
| Network Only |
Apenas rede |
Requisições não GET, dados em tempo 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();
Acesso síncrono em Workers
// Em um Worker — OPFS síncrono (extremamente 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 |
| Permissão do usuário |
Não necessária |
Usuário deve selecionar diretório |
| Visibilidade |
Sistema de arquivos virtual interno do navegador |
Sistema de arquivos real do SO |
| Sync em Worker |
Suportado |
Não suportado |
| Persistência |
Permanente |
Depende de concessão do usuário |
| Caso de uso |
Processamento de arquivos temporários de alto desempenho |
Editores de arquivos, exportação |
Cota de armazenamento e persistência
Consultar cota disponível
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 persistência
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');
}
Navegadores podem remover automaticamente o armazenamento não persistente sob pressão. Chame navigator.storage.persist() para solicitar persistência.
Guia de decisão
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
Exemplos do mundo real
| Cenário |
Armazenamento recomendado |
Razão |
| Preferência de tema do usuário |
localStorage |
Chave-valor simples, entre páginas |
| Rascunho de formulário |
sessionStorage |
Temporário, escopo da aba |
| Dados de ferramenta offline |
IndexedDB |
Estruturado, consultas indexadas |
| Cache de recursos estáticos |
Cache API |
Projetado para Request/Response |
| Arquivos temporários de processamento de vídeo |
OPFS |
E/S síncrona em Worker, alto desempenho |
Considerações de segurança
- Risco XSS: localStorage é legível por XSS — nunca armazene tokens sensíveis
- Cookies HttpOnly: Tokens de autenticação devem usar cookies HttpOnly + Secure
- Criptografia de armazenamento: Criptografe dados sensíveis com JWT ou Hash antes de armazenar
- Isolamento de mesma origem: Todo armazenamento segue a política de mesma origem; acesso entre origens é bloqueado
Resumo
O armazenamento do navegador evoluiu de um único localStorage para um sistema completo que abrange pares chave-valor, dados estruturados, cache HTTP e sistemas de arquivos. A chave para a seleção é corresponder às características dos dados: configuração simples → localStorage, dados estruturados → IndexedDB, cache HTTP → Cache API, E/S de arquivos de alto desempenho → OPFS.
Use o Analisador de Cookies para inspecionar a configuração de cookies, a Ferramenta JWT para verificar a segurança de tokens, e a Ferramenta Hash para hashear dados sensíveis.