Práticas recomendadas para usar o IndexedDB

Conheça as práticas recomendadas para sincronizar o estado do aplicativo entre o IndexedDB, uma biblioteca de gerenciamento de estado muito usada.

Quando um usuário carrega um site ou aplicativo pela primeira vez, geralmente há muito trabalho envolvido na construir o estado inicial do aplicativo que é usado para renderizar a interface; Por exemplo, às vezes o app precisa autenticar o usuário do lado do cliente e fazer várias solicitações de API antes de ter todas os dados que ele precisa exibir na página.

Armazenar o estado do aplicativo em O IndexedDB pode ser uma ótima maneira de acelerar o tempo de carregamento para visitas repetidas. O app pode ser sincronizado com qualquer serviço da API em segundo plano e atualiza a interface com novos dados lentamente, empregando uma stale-while- revalidate.

Outro bom uso para o IndexedDB é armazenar conteúdo gerado pelo usuário, seja como um armazenamento temporário antes de carregá-los no servidor, como cache de dados remotos no lado do cliente ou em ambos.

No entanto, ao usar o IndexedDB, há muitas coisas importantes a considerar que podem não ser imediatamente óbvias para desenvolvedores novos nas APIs. Este artigo responde a perguntas comuns e discute algumas das coisas mais importantes a serem lembradas ao se manter dados no IndexedDB.

Como manter seu app previsível

Muitas das complexidades em torno do IndexedDB se devem ao fato de que há tantos fatores que você (o desenvolvedor) não têm controle sobre. Esta seção explora muitos dos problemas que você precisa ter em mente ao trabalhar com IndexedDB.

Nem tudo pode ser armazenado no IndexedDB em todas as plataformas

Se você armazena arquivos grandes gerados pelo usuário, como imagens ou vídeos, tente armazenar como objetos File ou Blob. Isso funciona em algumas plataformas, mas falha em outras. Safari ativado O iOS, em especial, não pode armazenar Blobs no IndexedDB.

Felizmente, não é muito difícil converter um Blob em um ArrayBuffer e vice-versa. Armazenamento ArrayBuffers no IndexedDB são muito compatíveis.

No entanto, lembre-se de que uma Blob tem um tipo MIME, e uma ArrayBuffer não. Você precisará armazenar o tipo junto com o buffer para fazer a conversão corretamente.

Para converter um ArrayBuffer em um Blob, basta usar o construtor Blob.

function arrayBufferToBlob(buffer, type) {
  return new Blob([buffer], { type: type });
}

A outra direção é um pouco mais complexa e é um processo assíncrono. Você pode usar um Objeto FileReader para ler o blob como um ArrayBuffer. Quando a leitura terminar, um loadend é acionado no leitor. É possível unir esse processo em um Promise da seguinte forma:

function blobToArrayBuffer(blob) {
  return new Promise((resolve, reject) => {
    const reader = new FileReader();
    reader.addEventListener('loadend', () => {
      resolve(reader.result);
    });
    reader.addEventListener('error', reject);
    reader.readAsArrayBuffer(blob);
  });
}

A gravação no armazenamento pode falhar

Os erros ao gravar no IndexedDB podem ocorrer por diversos motivos e, em alguns casos, estão fora do seu controle como desenvolvedor. Por exemplo, alguns navegadores não permitem gravar no IndexedDB quando estiver no modo de navegação privada. Também existe a possibilidade de um usuário estar em um dispositivo que está quase sem espaço em disco, e a não permite que você armazene nada.

Por isso, é de extrema importância que você sempre implemente o tratamento de erros adequado nas Código IndexedDB. Isso também significa que geralmente é uma boa ideia manter o estado do aplicativo na memória além de armazená-lo), para que a interface não falhe ao executar no modo de navegação privada ou quando não houver espaço de armazenamento disponível, mesmo que alguns dos outros recursos do app que exigem armazenamento não funcionam.

Para encontrar erros nas operações do IndexedDB, adicione um manipulador de eventos para o evento error. sempre que você cria um objeto IDBDatabase, IDBTransaction ou IDBRequest.

const request = db.open('example-db', 1);
request.addEventListener('error', (event) => {
  console.log('Request error:', request.error);
};

Os dados armazenados podem ter sido modificados ou excluídos pelo usuário

Ao contrário dos bancos de dados do lado do servidor, em que é possível restringir o acesso não autorizado, os bancos de dados do lado do cliente são podem ser acessadas por extensões do navegador e ferramentas do desenvolvedor e podem ser apagadas pelo usuário.

Embora seja incomum os usuários modificarem os dados armazenados localmente, é bastante comum que eles para limpá-lo. É importante que seu aplicativo possa lidar com esses dois casos sem gerar erros.

Os dados armazenados podem estar desatualizados

Assim como na seção anterior, mesmo que o usuário não tenha modificado os dados, também talvez os dados armazenados nele tenham sido gravados por uma versão antiga do código, possivelmente um versão com bugs.

O IndexedDB tem suporte integrado para versões de esquema e upgrade por meio do IDBOpenDBRequest.onupgradeneeded() método No entanto, você ainda precisa escrever o código de upgrade de forma que ele possa lidar com o de uma versão anterior (incluindo uma versão com um bug).

Os testes de unidade podem ser muito úteis aqui, já que muitas vezes não é viável testar manualmente todos os e casos de upgrade.

Como manter a performance do seu app

Um dos principais recursos do IndexedDB é sua API assíncrona, mas não se engane com isso. pensando que você não precisa se preocupar com o desempenho ao usá-lo. Há vários casos em que o uso inadequado ainda pode bloquear a linha de execução principal, o que pode levar a instabilidade e falta de resposta.

Como regra geral, as leituras e gravações no IndexedDB não podem ser maiores que o necessário para os dados que está sendo acessado.

Embora o IndexedDB permita armazenar objetos grandes e aninhados como um único registro (e fazer isso é bem conveniente do ponto de vista do desenvolvedor), essa prática deve ser evitada. A porque, quando o IndexedDB armazena um objeto, ele deve primeiro criar um clone estruturado desse objeto, e o processo de clonagem estruturado acontece na linha de execução principal. Quanto maior a objeto, maior será o tempo de bloqueio.

Isso apresenta alguns desafios ao planejar como manter o estado do aplicativo no IndexedDB, já que a maioria das bibliotecas de gerenciamento de estado conhecidas (como o Redux) funcionam gerenciando suas toda a árvore de estado como um único objeto JavaScript.

Embora gerenciar o estado dessa forma tenha muitos benefícios (por exemplo, facilitar a compreensão do código e depuração) e, embora o simples armazenamento de toda a árvore de estado como um único registro no IndexedDB possa ser tentador e conveniente, fazer isso após cada mudança (mesmo se limitada/com rebaixamento) resultará em bloqueio desnecessário da linha de execução principal, isso aumentará a probabilidade de erros de gravação em alguns casos, isso fará com que a guia do navegador falhe ou pare de responder.

Em vez de armazenar toda a árvore de estado em um único registro, é necessário dividi-la em partes e atualiza apenas aqueles que são realmente alterados.

O mesmo acontece se você armazena itens grandes, como imagens, músicas ou vídeos no IndexedDB. Armazenar cada item com a própria chave em vez de dentro de um objeto maior, para que você possa recuperar os dados estruturados sem pagar o custo de também recuperar o arquivo binário.

Como na maioria das práticas recomendadas, essa não é uma regra "tudo ou nada". Nos casos em que não for viável dividir um objeto de estado e gravar o conjunto mínimo de alterações, dividindo os dados em subárvores e somente gravá-los ainda é preferível do que sempre gravar toda a árvore de estado. Pequena melhorias são melhores do que nenhuma melhoria.

Por fim, você deve sempre medir o impacto no desempenho do código que você escreve. Embora seja verdade que gravações pequenas no IndexedDB têm um desempenho melhor do que gravações grandes Isso só importa se as gravações no IndexedDB que seu aplicativo está fazendo realmente causarem tarefas longas que bloqueiam a linha de execução principal e prejudicam a experiência do usuário. É importante medir para que você entender para que você está otimizando.

Conclusões

Os desenvolvedores podem utilizar mecanismos de armazenamento de clientes, como o IndexedDB, para melhorar a experiência do usuário a aplicação pela persistência do estado nas sessões, mas pela diminuição do tempo de leva para carregar o estado inicial em visitas repetidas.

Embora o uso correto do IndexedDB possa melhorar muito a experiência do usuário, o uso incorreto ou não lidar com casos de erro pode levar a apps corrompidos e usuários insatisfeitos.

Como o armazenamento do cliente envolve muitos fatores que estão fora do seu controle, é fundamental que o código esteja bem e lidar adequadamente com erros, mesmo os que podem parecer improváveis inicialmente.