Há muitas opções diferentes para armazenar dados no navegador. Qual delas é melhor para suas necessidades?
As conexões com a Internet podem ser instáveis ou inexistentes durante o deslocamento do usuário. É por isso que o suporte off-line e o desempenho confiável são recursos comuns em apps da Web progressivos. Mesmo em ambientes sem fio perfeitos, o uso criterioso de armazenamento em cache e outras técnicas de armazenamento pode melhorar significativamente 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 é possível armazenar? Como você evita que ele seja despejado?
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, na sigla em inglês).
- Para outros dados, use o IndexedDB (com um wrapper de promessas).
A IndexedDB, o OPFS e a API Cache Storage têm suporte em todos os navegadores modernos.
Eles são assíncronos e não bloqueiam a linha de execução principal. No entanto, há também uma variante síncrona
do OPFS disponível exclusivamente em workers da Web. Eles são
acessíveis pelo objeto window
, workers da Web e workers de serviço, o que
permite usá-los em qualquer lugar do código.
E os 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 desempenho significativos.
SessionStorage é específico da guia e tem o escopo da duração 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 pode conter apenas strings. Como é específico da guia, ele não é acessível por workers da Web ou workers de serviço.
O LocalStorage deve ser evitado porque é síncrono e bloqueia a linha de execução principal. Ele é limitado a cerca de 5 MB e pode conter apenas strings. O LocalStorage não pode ser acessado por workers da Web ou de serviço.
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 mais do que 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. Assim como LocalStorage e SessionStorage, os cookies são limitados a strings.
A API File System Access foi projetada para permitir que os usuários leiam e editem arquivos no sistema de arquivos local. O usuário precisa conceder permissão antes que uma página possa ler ou gravar em qualquer arquivo local. Além disso, 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 você precisa abrir um arquivo, modificá-lo e, possivelmente, salvar as alterações no arquivo.
A API File System e a API FileWriter oferecem métodos para ler e gravar arquivos em um sistema de arquivos em sandbox. Embora seja assíncrono, ele não é recomendado porque está disponível apenas 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 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. É possível usar a API StorageManager para determinar a cota máxima disponível. Outros navegadores baseados em
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 tiver ativado a opção "Limpar cookies e dados de sites ao fechar todas as janelas" no Chrome, a cota de armazenamento será reduzida significativamente 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 de eTLD+1 (por exemplo,
example.com
,www.example.com
efoo.bar.example.com
) podem usar até 2 GB. Você pode usar a API StorageManager para determinar quanto espaço ainda está disponível. - O Safari (em computadores e dispositivos móveis) parece permitir cerca de 1 GB. Quando o limite
for atingido, o Safari vai pedir permissão 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 no Safari para dispositivos móveis, ele 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 de um PWA instalado é atingida, não parece haver nenhuma maneira de solicitar mais armazenamento.
No passado, se um site excedesse um determinado limite de dados armazenados, o navegador pedia ao usuário a permissão para usar mais dados. Por exemplo, se a origem usasse mais de 50 MB, o navegador pediria ao usuário permissão para armazenar até 100 MB e depois pediria novamente em incrementos de 50 MB.
Atualmente, a maioria dos navegadores modernos não solicita autorização ao usuário e permite que um site use até a cota atribuída. A exceção parece ser o Safari, que pede permissão para aumentar a cota alocada quando a cota de armazenamento é excedida. Se uma origem tentar usar mais do que a cota atribuída, as tentativas de gravar dados vão falhar.
Como posso verificar quanto espaço de armazenamento está disponível?
Em muitos navegadores, é possível usar a API StorageManager para determinar a quantidade de armazenamento disponível para a origem e a quantidade de armazenamento que ela está usando. Ele informa o número total de bytes usados pelo IndexedDB e pela API Cache e permite calcular o espaço de armazenamento 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.`);
}
É preciso detectar erros de cota excedida (confira 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 as Ferramentas do desenvolvedor 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 Application e Storage, ative a caixa de seleção Simulate custom storage quota e insira qualquer número válido para simular a cota de armazenamento.
Enquanto trabalhava neste guia, criei uma ferramenta simples para tentar usar rapidamente o máximo de armazenamento possível. É uma maneira rápida de testar diferentes mecanismos de armazenamento e conferir o que acontece quando você usa toda a cota.
Como lidar com o excesso de cota?
O que você deve fazer quando ultrapassar a cota? O mais importante é
sempre detectar e processar erros de gravação, seja um QuotaExceededError
ou
outra coisa. Em seguida, dependendo do design do app, decida como lidar com isso.
Por exemplo, exclua conteúdo que não foi acessado há muito tempo, remova
dados com base no tamanho ou ofereça uma maneira de os usuários escolherem o que querem excluir.
Tanto o IndexedDB quanto a API Cache geram uma DOMError
chamada
QuotaExceededError
quando você excede a cota disponível.
IndexedDB
Se a origem tiver excedido a cota, as tentativas de gravação no IndexedDB
vão falhar. O manipulador onabort()
da transação será chamado, transmitindo um evento.
O evento vai incluir um DOMException
na propriedade de erro. A verificação do
erro name
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 tiver excedido a cota, as tentativas de gravação na API Cache
serão rejeitadas com um 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: "Best Effort" e "Persistent". Melhor esforço significa que o armazenamento pode ser apagado pelo navegador sem interromper o usuário, mas é menos durável para dados de longo prazo ou críticos. O armazenamento persistente não é apagado automaticamente quando a capacidade de armazenamento fica baixa. O usuário precisa limpar esse armazenamento manualmente (nas configurações do navegador).
Por padrão, os dados de um site (incluindo IndexedDB, Cache API etc.) se enquadram na categoria de melhor esforço, o que significa que, a menos que um site tenha solicitado armazenamento permanente, o navegador pode remover os dados do site a seu critério, por exemplo, quando o armazenamento do dispositivo está baixo.
A política de remoção para o melhor esforço é:
- Os navegadores baseados em Chromium começam a remover dados quando o espaço deles acaba, limpando todos os dados do site da origem menos usada primeiro, depois da próxima, até que o navegador não ultrapasse mais o limite.
- O Firefox vai começar a remover dados quando o espaço em disco disponível estiver cheio, limpando todos os dados do site da origem menos usada recentemente primeiro, depois a próxima, até que o navegador não ultrapasse mais o limite.
- O Safari não removia os dados, mas recentemente implementou 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 para todo armazenamento gravável por script, incluindo IndexedDB, registro de service workers e a API Cache. Isso significa que o Safari vai remover todo o conteúdo do cache após sete dias de uso 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 muito mais no blog do WebKit para conferir todos os detalhes.
Buckets de armazenamento
A ideia principal da API Storage Buckets é permitir que os sites criem vários buckets de armazenamento, em que o navegador pode excluir cada bucket de forma independente. Isso permite que os desenvolvedores especifiquem a priorização de exclusão para garantir que os dados mais valiosos não sejam excluídos.
Bônus: por que usar um wrapper para IndexedDB
A 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 promessas, ela é baseada em eventos. Os wrappers de promessas, como idb para IndexedDB, ocultam alguns dos recursos avançados, mas, mais importante, ocultam a maquinaria complexa (por exemplo, transações, controle de versões de esquemas) que vem com a biblioteca IndexedDB.
Bônus: SQLite Wasm
Depois que o Web SQL foi descontinuado e removido do Chrome, o Google trabalhou com os mantenedores do popular banco de dados SQLite para oferecer uma substituição para o Web SQL baseada no SQLite. Leia SQLite Wasm no navegador com suporte do Origin Private File System para saber como usá-lo.
Conclusão
O armazenamento limitado e a necessidade de armazenar cada vez mais dados são coisas do passado. Os sites podem armazenar de forma eficaz todos os recursos e dados necessários para funcionamento. Usando a API StorageManager, você pode determinar quanto espaço está disponível e quanto você usou. E com o armazenamento persistente, a menos que o usuário o remova, você pode protegê-lo contra a exclusão.
Outros recursos
Obrigado
Agradecimentos especiais a Jarryd Goodman, Phil Walton, Eiji Kitamura, Daniel Murphy, Darwin Huang, Josh Bell, Marijn Kruisselbrink e Victor Costan pela revisão deste guia. Agradecemos a Eiji Kitamura, Addy Osmani e Marc Cohen, que escreveram os artigos originais que serviram de base para este. Eiji criou 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 particular de origem, buckets de armazenamento, SQLite Wasm e uma atualização geral do conteúdo em 2024.
A imagem principal é de Guillaume Bolduc no Unsplash.