Há muitas opções diferentes para armazenar dados no navegador. Qual é a melhor para suas necessidades?
As conexões de Internet podem ser instáveis ou inexistentes em trânsito. Por isso, o suporte off-line e a performance confiável são recursos comuns em apps da Web progressivos. Mesmo em ambientes sem fio perfeitos, o uso criterioso de técnicas de armazenamento em cache e outras técnicas de armazenamento pode melhorar substancialmente a experiência do usuário. Há várias maneiras de armazenar em cache os recursos estáticos do aplicativo (HTML, JavaScript, CSS, imagens etc.) e dados (dados do usuário, artigos de notícias etc.). Mas qual é a melhor solução? Quanto você pode armazenar? Como evitar que ele seja removido?
O que devo usar?
Confira uma recomendação geral para armazenar recursos:
- Para os recursos de rede necessários para carregar seu app, use a API Cache Storage (parte dos service workers).
- Para conteúdo baseado em arquivos, use o Origin Private File System (OPFS).
- Para outros dados, use IndexedDB (com um wrapper de promises).
O IndexedDB, o OPFS e a API Cache Storage são compatíveis com todos os navegadores modernos.
Eles são assíncronos e não bloqueiam a linha de execução principal (mas também há uma variante síncrona do OPFS que está disponível exclusivamente em web workers). Eles podem ser acessados pelo objeto window, web workers e service workers, o que permite usá-los em qualquer lugar do código.
E quanto a outros mecanismos de armazenamento?
Há vários outros mecanismos de armazenamento disponíveis no navegador, mas eles têm uso limitado e podem causar problemas de performance significativos.
SessionStorage é específico da guia e tem escopo para a vida útil da guia. Ele pode ser útil para armazenar pequenas quantidades de informações específicas da sessão, por exemplo, uma chave IndexedDB. Ele precisa ser usado com cuidado porque é síncrono e bloqueia a linha de execução principal. Ele é limitado a cerca de 5 MB e só pode conter strings. Como é específico da guia, ele não pode ser acessado por web workers ou service workers.
O LocalStorage precisa ser evitado porque é síncrono e bloqueia a linha de execução principal. Ele é limitado a cerca de 5 MB e só pode conter strings. O LocalStorage não pode ser acessado por web workers ou service workers.
Os cookies têm usos, mas não devem ser usados para armazenamento. Os cookies são enviados com cada solicitação HTTP. Portanto, armazenar qualquer coisa além de uma pequena quantidade de dados aumenta significativamente o tamanho de cada solicitação da Web. Eles são síncronos e não podem ser acessados por web workers. Como LocalStorage e SessionStorage, os cookies são limitados apenas a strings.
A API File System Access foi projetada para tornar possível que os usuários leiam e editem arquivos no sistema de arquivos local. O usuário precisa conceder permissão para que uma página possa ler ou gravar em qualquer arquivo local, e as permissões não são mantidas entre sessões, a menos que um identificador de arquivo seja armazenado em cache no IndexedDB. A API File System Access é mais adequada para casos de uso como editores, em que é necessário abrir um arquivo, modificá-lo e, possivelmente, salvar as alterações no arquivo.
A API File System e a API FileWriter fornecem métodos para ler e gravar arquivos em um sistema de arquivos em sandbox. Embora seja assíncrono, não é recomendado porque só está disponível em navegadores baseados no Chromium.
Quanto posso armazenar?
Em resumo, muito, pelo menos algumas centenas de megabytes e, potencialmente, centenas de gigabytes ou mais. As implementações do navegador variam, mas a quantidade de armazenamento disponível geralmente é baseada na quantidade de armazenamento disponível no dispositivo.
- O Chrome permite que o navegador use até 80% do espaço total em disco. Uma origem pode usar até 60% do espaço total em disco. Você pode usar a StorageManager
API para determinar a cota máxima disponível. Outros navegadores baseados no Chromium podem ser diferentes.
- No modo de navegação anônima, o Chrome reduz a quantidade de armazenamento que uma origem pode usar para aproximadamente 5% do espaço total em disco.
- Se o usuário ativou a opção "Limpar cookies e dados de sites ao fechar todas as janelas" no Chrome, a cota de armazenamento é significativamente reduzida para um máximo de aproximadamente 300 MB.
- O Firefox permite que o navegador use até 50% do espaço livre em disco. Um
grupo
eTLD+1 (por exemplo,
example.com,www.example.comefoo.bar.example.com) pode usar até 2 GB. Você pode usar a API StorageManager para determinar quanto espaço ainda está disponível. - O Safari (para computadores e dispositivos móveis) parece permitir cerca de 1 GB. Quando o limite é atingido, o Safari solicita ao usuário, aumentando o limite em incrementos de 200 MB. Não consegui encontrar nenhuma documentação oficial sobre isso.
- Se um PWA for adicionado à tela inicial do Safari para dispositivos móveis, ele vai criar um novo contêiner de armazenamento, e nada será compartilhado entre o PWA e o Safari para dispositivos móveis. Depois que a cota for atingida para um PWA instalado, não haverá como solicitar armazenamento adicional.
No passado, se um site excedesse um determinado limite de dados armazenados, o navegador solicitava ao usuário permissão para usar mais dados. Por exemplo, se a origem usasse mais de 50 MB, o navegador solicitava ao usuário que permitisse armazenar até 100 MB e, em seguida, perguntava novamente em incrementos de 50 MB.
Atualmente, a maioria dos navegadores modernos não solicita ao usuário e permite que um site use até a cota alocada. A exceção parece ser o Safari, que solicita quando a cota de armazenamento é excedida, pedindo permissão para aumentar a cota alocada. Se uma origem tentar usar mais do que a cota alocada, outras tentativas de gravar dados vão falhar.
Como posso verificar quanto armazenamento está disponível?
Em muitos navegadores, você pode usar a API StorageManager para determinar a quantidade de armazenamento disponível para a origem e quanto armazenamento ela está usando. Ela informa o número total de bytes usados pelo IndexedDB e pela API Cache, e permite calcular o espaço de armazenamento restante aproximado disponível.
if (navigator.storage && navigator.storage.estimate) {
const quota = await navigator.storage.estimate();
// quota.usage -> Number of bytes used.
// quota.quota -> Maximum number of bytes available.
const percentageUsed = (quota.usage / quota.quota) * 100;
console.log(`You've used ${percentageUsed}% of the available storage.`);
const remaining = quota.quota - quota.usage;
console.log(`You can write up to ${remaining} more bytes.`);
}
É necessário detectar erros de cota excedida (consulte abaixo). Em alguns casos, é possível que a cota disponível exceda a quantidade real de armazenamento disponível.
Inspecionar
Durante o desenvolvimento, você pode usar o DevTools do navegador para inspecionar os diferentes tipos de armazenamento e limpar todos os dados armazenados.
Um novo recurso foi adicionado no Chrome 88 que permite substituir a cota de armazenamento do site no painel de armazenamento. Esse recurso permite simular diferentes dispositivos e testar o comportamento dos apps em cenários de baixa disponibilidade de disco. Acesse Aplicativo e Armazenamento, marque a caixa de seleção Simular cota de armazenamento personalizada e insira qualquer número válido para simular a cota de armazenamento.
Ao trabalhar neste guia, escrevi uma ferramenta simples para tentar usar o máximo de armazenamento possível rapidamente. É uma maneira rápida de fazer experimentos com diferentes mecanismos de armazenamento e ver o que acontece quando você usa toda a cota.
Como lidar com a cota excedida?
O que você precisa fazer quando a cota é excedida? O mais importante é sempre detectar e processar erros de gravação, seja um QuotaExceededError ou algo mais. Em seguida, dependendo do design do app, decida como lidar com ele.
Por exemplo, exclua conteúdo que não foi acessado há muito tempo, remova dados com base no tamanho ou ofereça aos usuários uma maneira de escolher o que eles querem excluir.
O IndexedDB e a API Cache geram um DOMError chamado QuotaExceededError quando você excede a cota disponível.
IndexedDB
Se a origem excedeu a cota, as tentativas de gravar no IndexedDB vão falhar. O handler onabort() da transação será chamado, transmitindo um evento.
O evento vai incluir uma DOMException na propriedade de erro. A verificação do name de erro vai retornar QuotaExceededError.
const transaction = idb.transaction(['entries'], 'readwrite');
transaction.onabort = function(event) {
const error = event.target.error; // DOMException
if (error.name == 'QuotaExceededError') {
// Fallback code goes here
}
};
API Cache
Se a origem excedeu a cota, as tentativas de gravar na API Cache serão rejeitadas com uma DOMException QuotaExceededError.
try {
const cache = await caches.open('my-cache');
await cache.add(new Request('/sample1.jpg'));
} catch (err) {
if (error.name === 'QuotaExceededError') {
// Fallback code goes here
}
}
Como funciona a remoção?
O armazenamento da Web é categorizado em dois buckets: "Melhor esforço" e "Persistente". "Melhor esforço" significa que o armazenamento pode ser limpo pelo navegador sem interromper o usuário, mas é menos durável para dados críticos ou de longo prazo. O armazenamento persistente não é limpo automaticamente quando o armazenamento está baixo. O usuário precisa limpar manualmente esse armazenamento (nas configurações do navegador).
Por padrão, os dados de um site (incluindo IndexedDB, API Cache etc.) se enquadram na categoria de melhor esforço, o que significa que, a menos que um site tenha solicitado armazenamento persistente, o navegador poderá remover os dados do site a seu critério, por exemplo, quando o armazenamento do dispositivo estiver baixo.
A política de remoção para o melhor esforço é:
- Os navegadores baseados no Chromium vão começar a remover dados quando o navegador ficar sem espaço, limpando todos os dados do site da origem menos usada recentemente primeiro e, em seguida, a próxima, até que o navegador não esteja mais acima do limite.
- O Firefox vai começar a remover dados quando o espaço livre em disco disponível estiver cheio, limpando todos os dados do site da origem menos usada recentemente primeiro e, em seguida, a próxima, até que o navegador não esteja mais acima do limite.
- O Safari não removia dados, mas implementou recentemente um novo limite de sete dias em todo o armazenamento gravável (consulte abaixo).
A partir do iOS e iPadOS 13.4 e do Safari 13.1 no macOS, há um limite de sete dias em todo o armazenamento gravável de scripts, incluindo IndexedDB, registro de service worker e API Cache. Isso significa que o Safari vai remover todo o conteúdo do cache após sete dias de uso do Safari se o usuário não interagir com o site. Essa política de remoção não se aplica a PWAs instalados que foram adicionados à tela inicial. Consulte Bloqueio completo de cookies de terceiros e mais no blog do WebKit para detalhes completos.
Buckets de armazenamento
A ideia principal da API Storage Buckets é conceder aos sites a capacidade de criar vários buckets de armazenamento, em que o navegador pode optar por excluir cada bucket de forma independente de outros buckets. Isso permite que os desenvolvedores especifiquem a priorização da remoção para garantir que os dados mais valiosos não sejam excluídos.
Bônus: por que usar um wrapper para IndexedDB
O IndexedDB é uma API de baixo nível que exige uma configuração significativa antes do uso, o que pode ser particularmente difícil para armazenar dados de baixa complexidade. Ao contrário da maioria das APIs modernas baseadas em promises, ela é baseada em eventos. Wrappers de promises como idb para IndexedDB ocultam alguns dos recursos avançados, mas mais importante, ocultam a máquina complexa (por exemplo, transações, controle de versão de esquema) que acompanha a biblioteca IndexedDB.
Bônus: SQLite Wasm
Depois que o Web SQL foi descontinuado e removido do Chrome, o Google trabalhou com os responsáveis pela manutenção do popular banco de dados SQLite para oferecer uma substituição para o Web SQL com base no SQLite. Leia SQLite Wasm no navegador com suporte do Origin Private File System para detalhes sobre como usá-lo.
Conclusão
Acabaram os dias de armazenamento limitado e de solicitar ao usuário que armazene cada vez mais dados. Os sites podem armazenar efetivamente todos os recursos e dados necessários para serem executados. Usando a API StorageManager, você pode determinar quanto está disponível e quanto você usou. E, com armazenamento persistente, a menos que o usuário o remova, você pode protegê-lo contra remoção.
Outros recursos
Obrigado
Agradecimentos especiais a Jarryd Goodman, Phil Walton, Eiji Kitamura, Daniel Murphy, Darwin Huang, Josh Bell, Marijn Kruisselbrink e Victor Costan por revisarem este guia. Agradecemos a Eiji Kitamura, Addy Osmani e Marc Cohen, que escreveram os artigos originais em que este guia se baseia. Eiji escreveu uma ferramenta útil chamada Browser Storage Abuser, que foi útil para validar o comportamento atual. Ela permite armazenar o máximo de dados possível e conferir os limites de armazenamento no navegador. Agradecemos a François Beaufort, que fez a pesquisa no Safari para descobrir os limites de armazenamento, e a Thomas Steiner por adicionar informações sobre o sistema de arquivos privado de origem, buckets de armazenamento, SQLite Wasm e uma atualização geral de conteúdo em 2024.