Armazenamento para a Web

Há muitas opções diferentes para armazenar dados no navegador. Qual é a melhor opção para você?

As conexões de Internet podem ser instáveis ou inexistentes em qualquer lugar. É por isso que o suporte off-line e o desempenho confiável são recursos comuns em Progressive Web Apps. Mesmo em ambientes sem fio perfeitos, o uso criterioso 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 de aplicativos (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 você evita que ele seja removido?

O que devo usar?

Confira uma recomendação geral para armazenar recursos:

O IndexedDB e a API Cache Storage são compatíveis com todos os navegadores modernos. Ambos são assíncronos e não bloqueiam a linha de execução principal. Eles são acessíveis a partir do objeto window, de workers da Web e de service workers, o que facilita o uso deles em qualquer lugar do código.

E os outros mecanismos de armazenamento?

Existem vários outros mecanismos de armazenamento disponíveis no navegador, mas eles têm uso limitado e podem causar problemas significativos de desempenho.

SessionStorage é específica da guia e tem como escopo a vida útil dela. Isso pode ser útil para armazenar pequenas quantidades de informações específicas da sessão, por exemplo, uma chave IndexedDB. Ela precisa ser usada com cautela, já que é síncrona e vai bloquear a linha de execução principal. Ele é limitado a cerca de 5 MB e pode conter apenas strings. Por ser específico de guia, ele não pode ser acessado por workers da Web ou de service workers.

LocalStorage precisa ser evitado porque é síncrono e vai bloquear 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 seus usos, mas não devem ser usados para armazenamento. Os cookies são enviados a cada solicitação HTTP. Portanto, armazenar algo além de uma pequena quantidade de dados aumentará significativamente o tamanho de cada solicitação da Web. Eles são síncronos e não são acessíveis de web workers. Assim como LocalStorage e SessionStorage, os cookies são limitados a strings.

A API File System e a API FileWriter oferecem métodos para ler e gravar arquivos em um sistema de arquivos no modo sandbox. Embora seja assíncrono, ele não é recomendado, porque está disponível apenas em navegadores baseados no Chromium.

A API File System Access foi projetada para facilitar a leitura e edição de arquivos no sistema local dos usuários. O usuário precisa conceder permissão antes que uma página possa ler ou gravar em qualquer arquivo local, e as permissões não são mantidas entre as sessões.

O WebSQL não deve ser usado, e o uso existente deve ser migrado para o IndexedDB. A compatibilidade foi removida de quase todos os principais navegadores. O W3C deixou de manter a especificação Web SQL em 2010, e não há planos de futuras atualizações.

O cache de aplicativos não pode ser usado, e o uso atual precisa ser migrado para os service workers e a API Cache. Ela foi obsoleta, e o suporte será removido dos navegadores.

Quantos dados posso armazenar?

Resumindo, muitos, pelo menos algumas centenas de megabytes e possivelmente centenas de gigabytes ou mais. As implementações do navegador variam, mas a quantidade de armazenamento disponível geralmente se baseia 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. Use a API StorageManager 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 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.
    • Consulte o PR #3896 para ver detalhes sobre a implementação do Chrome.
  • O Internet Explorer 10 e versões posteriores podem armazenar até 250 MB e avisarão o usuário quando mais de 10 MB tiverem sido usados.
  • 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.com e foo.bar.example.com) podem usar até 2 GB. Use 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 for atingido, o Safari solicitará que o usuário aumente o limite em incrementos de 200 MB. Não encontrei nenhuma documentação oficial sobre isso.
    • Se um PWA for adicionado à tela inicial do Safari para dispositivos móveis, ele parecerá 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 é atingida para um PWA instalado, não há como solicitar armazenamento adicional.

Antes, se um site ultrapassasse um determinado limite de dados armazenados, o navegador solicitava que o usuário permitisse usar mais dados. Por exemplo, se a origem usar mais de 50 MB, o navegador vai solicitar que o usuário permita o armazenamento de até 100 MB e depois solicitará novamente em incrementos de 50 MB.

Atualmente, a maioria dos navegadores modernos não solicita o usuário e permite que um site use todo o limite de sua cota. A exceção parece ser o Safari, que avisa quando a cota de armazenamento é excedida e solicita permissão para aumentar a cota alocada. Se uma origem tentar usar mais do que a cota alocada, outras tentativas de gravar dados falharão.

Como posso verificar a quantidade de armazenamento 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 usada. 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.`);
}

O StorageManager ainda não foi implementado em todos os navegadores. Portanto, é necessário detectá-lo antes de usá-lo. Mesmo quando está disponível, você ainda precisa detectar erros de ultrapassagem de cota (veja 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 facilmente 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 um número válido para simular a cota de armazenamento.

Painel Storage do DevTools.

Enquanto trabalhava neste artigo, criei uma ferramenta simples para tentar usar rapidamente o máximo de armazenamento possível. É uma maneira rápida e fácil de testar diferentes mecanismos de armazenamento e ver o que acontece quando você usa toda a sua cota.

O que fazer quando ultrapassar a cota?

O que você precisa fazer ao ultrapassar a cota? O mais importante é sempre detectar e processar erros de gravação, sejam eles uma QuotaExceededError ou algo diferente. Em seguida, dependendo do design do app, decida como lidar com isso. Por exemplo, exclua conteúdo que não é acessado há muito tempo, remova dados com base no tamanho ou ofereça uma maneira de os usuários escolherem o que querem excluir.

O IndexedDB e 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 falharão. O gerenciador onabort() da transação será chamado, transmitindo um evento. O evento incluirá um DOMException na propriedade de erro. Verificar o erro name 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 exceder a cota, as tentativas de gravação na API Cache vão ser rejeitadas com um QuotaExceededError DOMException.

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". O melhor esforço significa que o armazenamento pode ser apagado pelo navegador sem interromper o usuário, mas é menos durável para dados críticos ou de longo prazo. O armazenamento permanente não é apagado automaticamente quando o armazenamento fica baixo. O usuário precisa limpar esse armazenamento manualmente 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 permanente, o navegador pode remover dados do site a critério próprio, por exemplo, quando o armazenamento do dispositivo estiver baixo.

A política de remoção para fazer o melhor esforço é:

  • Os navegadores baseados no Chromium vão começar a remover dados quando ficar sem espaço, limpando todos os dados do site da origem menos usada primeiro e depois a próxima, até que o navegador não esteja mais acima do limite.
  • O Internet Explorer 10+ não removerá dados, mas impedirá que a origem grave novamente.
  • O Firefox começará a remover dados quando o espaço em disco disponível for preenchido, limpando primeiro todos os dados do site da origem menos usada recentemente e depois a próxima, até que o navegador não esteja mais acima do limite.
  • Anteriormente, o Safari não removia dados, mas implementou um novo limite de sete dias em todo o armazenamento gravável (veja abaixo).

A partir do iOS e do iPadOS 13.4 e do Safari 13.1 no macOS, há um limite de sete dias para todo o armazenamento gravável de scripts, incluindo o IndexedDB, o registro do service worker e a API Cache. Isso significa que o Safari 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. Esta 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 ver mais detalhes.

Bônus: por que usar um wrapper para IndexedDB

A IndexedDB é uma API de baixo nível que requer configuração significativa antes do uso, o que pode ser particularmente difícil para armazenar dados simples. Ao contrário da maioria das APIs modernas baseadas em promessa, ele é baseado em eventos. Wrappers de promessas como idb para IndexedDB ocultam alguns dos recursos mais importantes, mas, mais importante, ocultam as máquinas complexas (por exemplo, transações, controle de versões de esquema) que vêm com a biblioteca IndexedDB.

Conclusão

Os dias de armazenamento limitado e solicitar que o usuário armazenasse cada vez mais dados ficavam no passado. Os sites podem armazenar efetivamente todos os recursos e dados necessários para serem executados. Com a API StorageManager, é possível determinar quanto está disponível para você e quanto foi usado. Com o armazenamento permanente, a menos que o usuário remova o arquivo, é possível protegê-lo contra remoção.

Outros recursos

Até logo!

Agradecimentos especiais a Jarryd Goodman, Phil Walton, Eiji Kitamura, Daniel Murphy, Darwin Huang, Josh Bell, Marijn Kruisselbrink e Victor Costan por analisar este artigo. Agradecemos a Eiji Kitamura, Addy Osmani e Marc Cohen, que escreveram os artigos originais nos quais este artigo se baseia. Eiji escreveu uma ferramenta útil chamada Abuser de armazenamento do navegador, que foi útil para validar o comportamento atual. Com ele, você pode armazenar o máximo de dados possível e conferir os limites de armazenamento no seu navegador. Graças a François Beaufort, que escavau no Safari para definir os limites de armazenamento.

A imagem principal é de Guillaume Bolduc no Unsplash.