HTML - Banco de Dados Indexado

-

Introdução ao IndexedDB

O IndexedDB é uma API de baixo nível para armazenamento de grandes quantidades de dados estruturados no lado do cliente em navegadores web. Ele oferece uma maneira de armazenar e recuperar dados localmente, permitindo que aplicações web funcionem offline e proporcionem uma boa experiência ao usuário.

O IndexedDB é um banco de dados NoSQL que utiliza um armazenamento de chave-valor e suporta transações e índices. Ele permite armazenar e recuperar objetos JavaScript, incluindo tipos de dados complexos como arrays e objetos aninhados. O IndexedDB oferece uma capacidade de armazenamento maior em comparação com outras opções de armazenamento do lado do cliente, como cookies e localStorage.

Uma das principais vantagens do uso do IndexedDB é sua capacidade de lidar bem com grandes quantidades de dados estruturados. É adequado para aplicações que necessitam de armazenamento local persistente, como aplicativos web offline, aplicativos web progressivos (PWAs) e aplicações com uso intensivo de dados. O IndexedDB oferece operações assíncronas, o que significa que não bloqueia a thread principal de execução, resultando em melhor desempenho e responsividade.

Em comparação com outros métodos de armazenamento como cookies e localStorage, o IndexedDB possui várias vantagens:

Aspecto IndexedDB Cookies localStorage
Capacidade de armazenamento Gigabytes Cerca de 4KB Geralmente até 5-10MB
Dados estruturados Suporta armazenamento de objetos JavaScript complexos e consultas eficientes usando índices Limitado ao armazenamento de pares chave-valor de strings Limitado ao armazenamento de pares chave-valor de strings
API assíncrona Operações são assíncronas, resultando em melhor desempenho e responsividade Síncrono, pode bloquear a thread principal Síncrono, pode bloquear a thread principal
Suporte a transações Fornece um modelo transacional para operações de dados, garantindo a integridade dos dados Sem suporte a transações Sem suporte a transações
Indexação e consulta Suporta a criação de índices em propriedades de object stores para pesquisa e consulta rápidas Sem capacidades de indexação ou consulta Sem capacidades de indexação ou consulta

Ao usar o IndexedDB, os desenvolvedores web podem criar aplicações que funcionam bem tanto online quanto offline, proporcionando uma boa experiência ao usuário e lidando com grandes quantidades de dados estruturados.

Conceitos Básicos

Para trabalhar com IndexedDB, você precisa entender alguns conceitos básicos. O IndexedDB é construído em torno de um armazenamento de chave-valor, o que significa que os dados são armazenados e recuperados usando chaves únicas associadas a cada valor.

No centro do IndexedDB estão os object stores (armazenamentos de objetos). Um object store é como uma tabela em um banco de dados tradicional e armazena os dados reais. Cada object store tem um nome e pode armazenar objetos JavaScript de qualquer tipo, incluindo estruturas de dados complexas. Os object stores são criados dentro de um banco de dados e são usados para organizar e agrupar dados relacionados.

Exemplo: Criando um Object Store

let request = indexedDB.open("MyDatabase", 1);

request.onupgradeneeded = function(event) {
  let db = event.target.result;
  let objectStore = db.createObjectStore("MyObjectStore", { keyPath: "id" });
};

As transações são uma parte fundamental do IndexedDB. Todas as operações de dados, como leitura, escrita ou exclusão de dados, devem acontecer dentro de uma transação. As transações fornecem uma maneira de agrupar várias operações e manter a integridade dos dados. Elas podem ser somente leitura ou leitura e escrita, dependendo do tipo de operações sendo realizadas. Se qualquer operação dentro de uma transação falhar, toda a transação é revertida e o banco de dados permanece inalterado.

Exemplo: Usando uma Transação

let db = event.target.result;
let transaction = db.transaction(["MyObjectStore"], "readwrite");
let objectStore = transaction.objectStore("MyObjectStore");

let request = objectStore.add({ id: 1, name: "John Doe" });

request.onsuccess = function(event) {
  console.log("Os dados foram adicionados ao seu banco de dados.");
};

request.onerror = function(event) {
  console.log("Não foi possível adicionar dados ao seu banco de dados.");
};

Os índices são outro conceito importante no IndexedDB. Um índice é uma maneira de pesquisar e recuperar dados de um object store com base em uma propriedade específica ou conjunto de propriedades. Os índices permitem que você encontre dados rapidamente sem ter que percorrer todo o object store. Você pode criar índices em qualquer propriedade dos objetos armazenados em um object store, e também pode criar índices compostos que combinam várias propriedades.

Exemplo: Criando um Índice

let objectStore = db.createObjectStore("MyObjectStore", { keyPath: "id" });
objectStore.createIndex("name", "name", { unique: false });

Configurando o IndexedDB

Antes de começar a usar o IndexedDB em sua aplicação web, você precisa configurá-lo. Isso envolve verificar o suporte do navegador, abrir um banco de dados, criar object stores e definir índices.

É importante verificar se o navegador suporta IndexedDB. Você pode fazer isso verificando se a propriedade indexedDB existe no objeto window.

Exemplo: Verificando o Suporte ao IndexedDB

if (window.indexedDB) {
  console.log("IndexedDB é suportado");
} else {
  console.log("IndexedDB não é suportado");
}

Após confirmar que o IndexedDB é suportado, o próximo passo é abrir um banco de dados. Você pode abrir um banco de dados chamando o método open() no objeto indexedDB. Este método recebe dois parâmetros: o nome do banco de dados e o número da versão. Se o banco de dados não existir, ele será criado; caso contrário, o banco de dados existente será aberto.

Exemplo: Abrindo um Banco de Dados

let request = indexedDB.open("MeuBancoDeDados", 1);

request.onerror = function(event) {
  console.log("Erro ao abrir o banco de dados");
};

request.onsuccess = function(event) {
  let db = event.target.result;
  console.log("Banco de dados aberto com sucesso");
};

Ao abrir um banco de dados, você também pode especificar um número de versão. Se o número da versão for maior que a versão existente, o evento onupgradeneeded será acionado, permitindo que você altere a estrutura do banco de dados, como criar ou modificar object stores e índices.

Dentro do manipulador de eventos onupgradeneeded, você pode criar object stores usando o método createObjectStore() no objeto do banco de dados. Você precisa fornecer um nome para o object store e especificar o key path, que é a propriedade que identifica exclusivamente cada objeto no armazenamento.

Exemplo: Criando Object Store

request.onupgradeneeded = function(event) {
  let db = event.target.result;
  let objectStore = db.createObjectStore("MeuObjectStore", { keyPath: "id" });
};

Depois de criar um object store, você pode definir índices em propriedades específicas dos objetos armazenados no object store. Os índices permitem que você pesquise e obtenha dados com base nessas propriedades. Para criar um índice, você pode usar o método createIndex() no object store.

Exemplo: Definindo Índices

request.onupgradeneeded = function(event) {
  let db = event.target.result;
  let objectStore = db.createObjectStore("MeuObjectStore", { keyPath: "id" });
  objectStore.createIndex("nome", "nome", { unique: false });
  objectStore.createIndex("idade", "idade", { unique: false });
};

Criamos dois índices: um na propriedade "nome" e outro na propriedade "idade". O parâmetro unique especifica se os valores do índice devem ser únicos ou não.

Realizando Operações CRUD

Criando Dados

Para adicionar dados a um armazenamento de objetos no IndexedDB, você precisa usar transações. Primeiro, abra uma transação no armazenamento de objetos desejado com o modo "readwrite". Depois, obtenha uma referência ao armazenamento de objetos usando o método objectStore() no objeto de transação. Por fim, use o método add() ou put() no armazenamento de objetos para adicionar os dados.

Exemplo: Adicionando dados ao IndexedDB

let db;
let request = indexedDB.open("MyDatabase", 1);

request.onsuccess = function(event) {
  db = event.target.result;
  addData({ id: 1, name: "John Doe", age: 25 });
};

function addData(data) {
  let transaction = db.transaction(["MyObjectStore"], "readwrite");
  let objectStore = transaction.objectStore("MyObjectStore");

  let request = objectStore.add(data);

  request.onsuccess = function(event) {
    console.log("Dados adicionados com sucesso");
  };

  request.onerror = function(event) {
    console.log("Erro ao adicionar dados");
  };
}

O método add() é usado para adicionar novos dados, enquanto o método put() pode ser usado para adicionar novos dados ou atualizar dados existentes se a chave já existir.

É importante lidar com erros que podem ocorrer durante o processo de adição de dados. Você pode usar o manipulador de eventos onerror no objeto de solicitação para capturar e tratar quaisquer erros.

Lendo Dados

Para obter dados de um armazenamento de objetos, você pode usar o método get() no armazenamento de objetos. Você precisa fornecer a chave dos dados que deseja obter.

Exemplo: Obtendo dados do IndexedDB

let transaction = db.transaction(["MyObjectStore"], "readonly");
let objectStore = transaction.objectStore("MyObjectStore");

let request = objectStore.get(1);

request.onsuccess = function(event) {
  let data = event.target.result;
  console.log("Dados recuperados:", data);
};

Se você quiser pesquisar dados com base em uma propriedade específica, pode usar índices. Primeiro, obtenha uma referência ao índice usando o método index() no armazenamento de objetos. Em seguida, use o método get() no índice para obter dados com base na chave do índice.

Exemplo: Usando índices para obter dados

let transaction = db.transaction(["MyObjectStore"], "readonly");
let objectStore = transaction.objectStore("MyObjectStore");
let index = objectStore.index("name");

let request = index.get("John Doe");

request.onsuccess = function(event) {
  let data = event.target.result;
  console.log("Dados recuperados:", data);
};

Para consultas mais avançadas ou para percorrer várias entradas de dados, você pode usar cursores. Os cursores permitem percorrer todos os dados em um armazenamento de objetos ou índice. Você pode abrir um cursor usando o método openCursor() no armazenamento de objetos ou índice.

Exemplo: Usando cursores no IndexedDB

let transaction = db.transaction(["MyObjectStore"], "readonly");
let objectStore = transaction.objectStore("MyObjectStore");

let request = objectStore.openCursor();

request.onsuccess = function(event) {
  let cursor = event.target.result;
  if (cursor) {
    console.log("Chave:", cursor.key);
    console.log("Dados:", cursor.value);
    cursor.continue();
  } else {
    console.log("Não há mais dados");
  }
};

No exemplo acima, abrimos um cursor no armazenamento de objetos. O manipulador de eventos onsuccess é chamado para cada entrada de dados. Podemos acessar a chave e o valor da entrada atual usando cursor.key e cursor.value, respectivamente. Para ir para a próxima entrada, chamamos o método continue() no cursor.

Atualizando Dados

Para atualizar dados existentes em um armazenamento de objetos, você pode usar o método put(). Ele funciona de maneira semelhante ao método add(), mas se a chave já existir, ele atualizará os dados correspondentes.

Exemplo: Atualizando dados no IndexedDB

let transaction = db.transaction(["MyObjectStore"], "readwrite");
let objectStore = transaction.objectStore("MyObjectStore");

let updatedData = { id: 1, name: "John Doe", age: 26 };
let request = objectStore.put(updatedData);

request.onsuccess = function(event) {
  console.log("Dados atualizados com sucesso");
};

Você também pode atualizar campos específicos de uma entrada de dados existente usando o método put(). Primeiro, obtenha os dados usando o método get(), atualize os campos desejados e, em seguida, use put() para salvar as alterações.

Exemplo: Atualizando parcialmente dados no IndexedDB

let transaction = db.transaction(["MyObjectStore"], "readwrite");
let objectStore = transaction.objectStore("MyObjectStore");

let request = objectStore.get(1);

request.onsuccess = function(event) {
  let data = event.target.result;
  data.age = 27;

  let updateRequest = objectStore.put(data);

  updateRequest.onsuccess = function(event) {
    console.log("Dados atualizados com sucesso");
  };
};

Ao atualizar dados, esteja atento ao versionamento e mudanças de esquema. Se você precisar alterar a estrutura de seus armazenamentos de objetos ou índices, deve lidar com isso no manipulador de eventos onupgradeneeded e versionar adequadamente seu banco de dados.

Excluindo Dados

Para remover dados de um armazenamento de objetos, você pode usar o método delete() no armazenamento de objetos. Você precisa fornecer a chave dos dados que deseja remover.

Exemplo: Excluindo dados do IndexedDB

let transaction = db.transaction(["MyObjectStore"], "readwrite");
let objectStore = transaction.objectStore("MyObjectStore");

let request = objectStore.delete(1);

request.onsuccess = function(event) {
  console.log("Dados removidos com sucesso");
};

Se você quiser limpar um armazenamento de objetos inteiro, pode usar o método clear() no armazenamento de objetos.

Exemplo: Limpando um armazenamento de objetos do IndexedDB

let transaction = db.transaction(["MyObjectStore"], "readwrite");
let objectStore = transaction.objectStore("MyObjectStore");

let request = objectStore.clear();

request.onsuccess = function(event) {
  console.log("Armazenamento de objetos limpo com sucesso");
};

Para excluir um banco de dados, você pode usar o método deleteDatabase() no objeto indexedDB. Isso removerá completamente o banco de dados e todos os seus armazenamentos de objetos.

Exemplo: Excluindo um banco de dados do IndexedDB

let request = indexedDB.deleteDatabase("MyDatabase");

request.onsuccess = function(event) {
  console.log("Banco de dados excluído com sucesso");
};

Tenha cuidado ao excluir dados, pois é uma operação permanente e não pode ser desfeita.

Recursos Avançados

O IndexedDB possui vários recursos avançados que permitem gerenciar e otimizar dados de maneiras mais complexas. Vamos examinar alguns desses recursos em detalhes.

Versionamento e Atualizações

O IndexedDB usa versionamento para gerenciar mudanças na estrutura do banco de dados ao longo do tempo. Cada banco de dados tem um número de versão associado a ele. Quando você abre um banco de dados com um número de versão maior que o atual, o evento onupgradeneeded é acionado, permitindo que você altere o esquema do banco de dados.

Exemplo: Versionamento e Atualizações

let request = indexedDB.open("MeuBancoDeDados", 2); // Atualizar para versão 2

request.onupgradeneeded = function(event) {
  let db = event.target.result;

  // Realizar mudanças no esquema do banco de dados
  if (event.oldVersion < 1) {
    // Criar object stores e índices para a versão 1
    let objectStore = db.createObjectStore("MeuObjectStore", { keyPath: "id" });
    objectStore.createIndex("nome", "nome", { unique: false });
  }

  if (event.oldVersion < 2) {
    // Fazer alterações para a versão 2
    let objectStore = db.createObjectStore("OutroObjectStore", { keyPath: "id" });
    objectStore.createIndex("categoria", "categoria", { unique: false });
  }
};

No exemplo acima, abrimos o banco de dados com a versão 2. Se a versão atual for menor que 2, o evento onupgradeneeded é acionado. Podemos então verificar a propriedade oldVersion para encontrar a versão atual e fazer as mudanças necessárias no esquema para cada atualização de versão.

Índices e Chaves Compostas

Os índices no IndexedDB permitem consultar e pesquisar dados de forma eficiente com base em propriedades específicas. Você pode criar índices em propriedades únicas ou criar índices compostos que combinam várias propriedades.

Exemplo: Índices e Chaves Compostas

let request = indexedDB.open("MeuBancoDeDados", 1);

request.onupgradeneeded = function(event) {
  let db = event.target.result;
  let objectStore = db.createObjectStore("MeuObjectStore", { keyPath: "id" });

  // Criar um índice de propriedade única
  objectStore.createIndex("nome", "nome", { unique: false });

  // Criar um índice composto
  objectStore.createIndex("nomeIdade", ["nome", "idade"], { unique: false });
};

No exemplo acima, criamos um índice de propriedade única na propriedade "nome" e um índice composto nas propriedades "nome" e "idade". Os índices compostos permitem consultar dados com base em várias propriedades ao mesmo tempo.

Transações e Concorrência

As transações no IndexedDB garantem que os dados estejam corretos e consistentes. Todas as operações de banco de dados devem ser feitas dentro de uma transação. As transações podem ser somente leitura ou leitura e escrita, dependendo do tipo de operações sendo realizadas.

O IndexedDB também suporta acesso concorrente ao banco de dados. Várias transações podem estar ativas ao mesmo tempo, mas elas operam em object stores diferentes ou têm modos de acesso diferentes (somente leitura ou leitura e escrita) para evitar conflitos.

Exemplo: Transações e Concorrência

let transacao1 = db.transaction(["ObjectStore1"], "readonly");
let transacao2 = db.transaction(["ObjectStore2"], "readwrite");

// Várias transações podem estar ativas simultaneamente
transacao1.objectStore("ObjectStore1").get(1);
transacao2.objectStore("ObjectStore2").add({ id: 1, nome: "João" });

No exemplo acima, temos duas transações ativas ao mesmo tempo. transacao1 é somente leitura e opera em "ObjectStore1", enquanto transacao2 é leitura e escrita e opera em "ObjectStore2". Elas podem prosseguir independentemente sem conflitos.

Considerações de Desempenho

Para otimizar o desempenho do uso do IndexedDB, considere o seguinte:

Consideração Descrição
Use índices apropriados Crie índices em propriedades que você frequentemente usa para consulta ou pesquisa. Os índices aceleram as operações de recuperação de dados.
Minimize o escopo das transações Mantenha as transações o menor possível e inclua apenas as operações necessárias. Isso ajuda a reduzir a duração do bloqueio e melhora a concorrência.
Use operações em lote Se você precisa realizar várias operações de escrita, considere usar operações em lote como add(), put(), ou delete() dentro de uma única transação. Isso minimiza a sobrecarga de criar transações separadas para cada operação.
Evite recuperação desnecessária de dados Obtenha apenas os dados necessários. Use índices e intervalos de chaves para reduzir o conjunto de resultados e evite obter dados desnecessários.
Lide com grandes conjuntos de dados de forma eficiente Se você está lidando com grandes quantidades de dados, considere usar técnicas como paginação ou carregamento lazy para carregar dados em partes menores conforme necessário.

Ao manter essas considerações de desempenho em mente e usar bem índices, transações e concorrência, você pode construir aplicações web eficientes e rápidas com o IndexedDB.

Segurança

Ao usar IndexedDB para armazenar dados em uma aplicação web, é importante pensar em segurança e proteger informações sensíveis. Aqui estão alguns aspectos fundamentais para lidar com dados sensíveis e garantir o acesso seguro ao IndexedDB:

Tratamento de Dados Sensíveis

Se sua aplicação lida com dados sensíveis, como informações pessoais ou detalhes financeiros, você precisa tomar precauções extras ao armazenar e manipular esses dados no IndexedDB. Aqui estão algumas práticas recomendadas:

  1. Criptografar dados sensíveis: Antes de armazenar informações sensíveis no IndexedDB, criptografe-as usando um algoritmo de criptografia forte. Dessa forma, mesmo que alguém obtenha acesso aos dados do IndexedDB, não conseguirá ler as informações sensíveis sem a chave de criptografia.

  2. Usar bibliotecas de criptografia seguras: Utilize bibliotecas e algoritmos de criptografia bem estabelecidos e confiáveis, como o AES (Advanced Encryption Standard), para criptografar e descriptografar dados sensíveis. Não crie seus próprios algoritmos de criptografia, pois eles podem conter vulnerabilidades.

  3. Armazenar chaves de criptografia com segurança: Mantenha as chaves de criptografia usadas para criptografar e descriptografar dados sensíveis em local seguro. Não as armazene no mesmo banco de dados que os dados criptografados. Em vez disso, considere usar técnicas como funções de derivação de chaves ou mecanismos seguros de armazenamento de chaves fornecidos pela plataforma ou navegador.

  4. Minimizar a retenção de dados: Armazene dados sensíveis no IndexedDB apenas quando absolutamente necessário. Se os dados não forem mais necessários, exclua-os com segurança do banco de dados. Revise e remova regularmente quaisquer informações sensíveis desnecessárias para reduzir o risco de violações de dados.

Garantindo o Acesso Seguro ao IndexedDB

Além de lidar com dados sensíveis de forma segura, você também deve controlar o acesso ao IndexedDB para evitar acessos ou alterações não autorizados. Aqui estão algumas medidas que você pode tomar:

  1. Usar recursos de segurança do navegador: Utilize recursos de segurança do navegador, como a Política de Mesma Origem e a Política de Segurança de Conteúdo (CSP), para restringir o acesso ao IndexedDB de fontes não confiáveis. Essas políticas ajudam a prevenir ataques de cross-site scripting (XSS) e acesso não autorizado ao IndexedDB de origens diferentes.

Exemplo de Política de Mesma Origem

<meta http-equiv="Content-Security-Policy" content="default-src 'self'">
  1. Implementar autenticação de usuário: Se sua aplicação requer autenticação de usuário, certifique-se de que apenas usuários autenticados possam acessar os dados do IndexedDB. Use mecanismos de autenticação seguros, como tokens ou gerenciamento de sessão, para verificar a identidade do usuário antes de conceder acesso ao banco de dados.

Exemplo de Gerenciamento Seguro de Sessão

// Exemplo de autenticação baseada em token
fetch('https://api.example.com/data', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer ' + token
  }
})
  .then(response => response.json())
  .then(data => console.log(data));
  1. Aplicar controles de acesso: Implemente controles de acesso baseados em funções ou permissões de usuário. Decida quais usuários ou grupos de usuários devem ter acesso de leitura ou gravação a object stores específicos ou dados dentro do IndexedDB. Use esses controles de acesso de forma consistente em toda a sua aplicação.

  2. Validar e sanitizar a entrada do usuário: Ao aceitar entradas do usuário que serão armazenadas no IndexedDB, valide e sanitize a entrada para evitar possíveis vulnerabilidades de segurança, como injeção de SQL ou ataques de cross-site scripting. Use regras de validação rigorosas e escape ou remova quaisquer caracteres ou scripts maliciosos.

Exemplo de Validação de Entrada do Usuário

const sanitizedInput = input.replace(/[<>]/g, ''); // sanitização simples
  1. Usar comunicação segura: Se sua aplicação se comunica com um servidor para sincronizar dados do IndexedDB, certifique-se de que o canal de comunicação seja seguro. Use HTTPS/SSL para criptografar os dados transmitidos entre o cliente e o servidor, evitando interceptação ou adulteração.

Exemplo de Comunicação HTTPS

<form action="https://seuservidor.com/enviar" method="post">
  <input type="text" name="data">
  <input type="submit" value="Enviar">
</form>

Lembre-se de que a segurança é um processo contínuo, e é importante manter-se atualizado com as melhores práticas e vulnerabilidades de segurança mais recentes. Revise e atualize regularmente suas medidas de segurança para proteger dados sensíveis e manter sua implementação do IndexedDB segura.

Compatibilidade com Navegadores e Alternativas

Ao usar IndexedDB em sua aplicação web, você deve pensar na compatibilidade com navegadores e fornecer estratégias alternativas para navegadores que podem não suportar IndexedDB ou ter suporte limitado.

O suporte dos navegadores ao IndexedDB melhorou com o tempo, com a maioria dos navegadores modernos agora oferecendo bom suporte para a API. No entanto, ainda pode haver alguns navegadores mais antigos ou dispositivos móveis que têm suporte limitado ou nenhum suporte ao IndexedDB.

Para verificar se um navegador suporta IndexedDB, você pode usar uma técnica simples de detecção de recursos:

Exemplo: Detecção de recursos para suporte ao IndexedDB

if (window.indexedDB) {
  console.log("IndexedDB é suportado");
} else {
  console.log("IndexedDB não é suportado");
}

Se o IndexedDB não for suportado em um navegador, você precisará fornecer estratégias alternativas para lidar com o armazenamento e recuperação de dados. Uma abordagem comum é usar outros mecanismos de armazenamento como alternativas, como Web Storage (localStorage ou sessionStorage) ou cookies. Essas alternativas podem ter limitações em comparação com o IndexedDB, como menor capacidade de armazenamento ou falta de recursos avançados como indexação e transações, mas ainda podem fornecer um nível básico de persistência de dados.

Exemplo: Estratégias alternativas para armazenamento de dados

if (window.indexedDB) {
  // Usar IndexedDB
  let request = indexedDB.open("MeuBancoDeDados", 1);
  // ...
} else if (window.localStorage) {
  // Alternativa para localStorage
  localStorage.setItem("chave", "valor");
  // ...
} else {
  console.log("Nenhum mecanismo de armazenamento disponível");
}

Outra opção é usar polyfills ou bibliotecas que fornecem uma API consistente para IndexedDB enquanto lidam internamente com diferenças entre navegadores e alternativas. Essas ferramentas visam preencher as lacunas no suporte dos navegadores e permitir que os desenvolvedores usem IndexedDB de uma maneira mais independente do navegador.

Alguns polyfills e bibliotecas populares para IndexedDB incluem:

Polyfill/Biblioteca Descrição
IndexedDBShim Um polyfill que adiciona suporte para IndexedDB em navegadores que não têm suporte nativo.
localForage Uma biblioteca que fornece uma API simples e consistente para armazenamento de dados do lado do cliente, incluindo IndexedDB, Web Storage e WebSQL.
Dexie.js Uma biblioteca wrapper para IndexedDB que fornece uma API mais intuitiva e concisa para trabalhar com IndexedDB.

Exemplo: Incluir o polyfill IndexedDBShim

<!-- Incluir o polyfill IndexedDBShim -->
<script src="indexeddbshim.min.js"></script>

Exemplo: Usar IndexedDB normalmente

// Usar IndexedDB normalmente
let request = indexedDB.open("MeuBancoDeDados", 1);
// ...

Ao usar polyfills ou bibliotecas, certifique-se de revisar sua documentação e considerar seu suporte a navegadores, impacto no desempenho e quaisquer limitações que possam ter.

Lembre-se de testar sua aplicação web em diferentes navegadores e dispositivos para garantir que as estratégias alternativas funcionem como previsto e forneçam uma boa experiência do usuário, mesmo em navegadores sem suporte ao IndexedDB.

Exemplos Práticos e Casos de Uso

O IndexedDB é uma ferramenta poderosa que pode ser usada em vários cenários práticos para melhorar a funcionalidade e a experiência do usuário em aplicações web. Vamos ver alguns casos de uso comuns e exemplos onde o IndexedDB se destaca.

Armazenamento e Sincronização de Dados Offline

Um dos principais benefícios do IndexedDB é sua capacidade de armazenar dados offline e permitir funcionalidade offline em aplicações web. Com o IndexedDB, você pode armazenar dados da aplicação localmente no dispositivo do usuário, permitindo que eles acessem e interajam com a aplicação mesmo quando não estão conectados à internet.

Exemplo: Armazenar dados offline

// Armazenar dados offline
function armazenarDadosOffline(dados) {
  let transacao = db.transaction(["MeuObjetoArmazenado"], "readwrite");
  let objetoArmazenado = transacao.objectStore("MeuObjetoArmazenado");

  let requisicao = objetoArmazenado.add(dados);

  requisicao.onsuccess = function(evento) {
    console.log("Dados armazenados offline");
  };
}

// Sincronizar dados quando online
function sincronizarDadosComServidor() {
  let transacao = db.transaction(["MeuObjetoArmazenado"], "readonly");
  let objetoArmazenado = transacao.objectStore("MeuObjetoArmazenado");

  let requisicao = objetoArmazenado.openCursor();

  requisicao.onsuccess = function(evento) {
    let cursor = evento.target.result;
    if (cursor) {
      let dados = cursor.value;
      enviarDadosParaServidor(dados);
      cursor.continue();
    } else {
      console.log("Todos os dados sincronizados com o servidor");
    }
  };
}

// Enviar dados para o servidor
function enviarDadosParaServidor(dados) {
  fetch('https://api.exemplo.com/dados', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json'
    },
    body: JSON.stringify(dados)
  })
    .then(resposta => {
      if (resposta.ok) {
        console.log("Dados sincronizados com o servidor");
      }
    });
}

A função armazenarDadosOffline guarda dados no IndexedDB quando a aplicação está offline. Os dados são adicionados a um objeto armazenado dentro de uma transação. Posteriormente, quando a aplicação volta a ficar online, a função sincronizarDadosComServidor usa um cursor para percorrer todos os dados armazenados e envia cada item de dados para o servidor usando a função enviarDadosParaServidor. Dessa forma, a aplicação pode funcionar offline e sincronizar os dados com o servidor quando a conexão é restaurada.

Armazenamento em Cache de Dados da Aplicação

O IndexedDB pode ser usado como um mecanismo de cache para armazenar localmente dados frequentemente acessados ou de carregamento lento. Ao armazenar dados em cache no IndexedDB, você pode melhorar o desempenho e a velocidade da sua aplicação web.

Exemplo: Verificar cache para dados

// Verificar cache para dados
function obterDadosDoCache(chave) {
  return new Promise((resolver, rejeitar) => {
    let transacao = db.transaction(["ArmazenamentoCache"], "readonly");
    let objetoArmazenado = transacao.objectStore("ArmazenamentoCache");

    let requisicao = objetoArmazenado.get(chave);

    requisicao.onsuccess = function(evento) {
      let dados = evento.target.result;
      if (dados) {
        console.log("Dados recuperados do cache");
        resolver(dados);
      } else {
        console.log("Dados não encontrados no cache");
        rejeitar();
      }
    };
  });
}

// Armazenar dados no cache
function armazenarDadosNoCache(chave, dados) {
  let transacao = db.transaction(["ArmazenamentoCache"], "readwrite");
  let objetoArmazenado = transacao.objectStore("ArmazenamentoCache");

  let requisicao = objetoArmazenado.put(dados, chave);

  requisicao.onsuccess = function(evento) {
    console.log("Dados armazenados no cache");
  };
}

// Obter dados do cache ou buscar do servidor
function obterDados(chave) {
  obterDadosDoCache(chave)
    .then(dados => {
      console.log("Dados recuperados do cache:", dados);
    })
    .catch(() => {
      fetch(`https://api.exemplo.com/dados/${chave}`)
        .then(resposta => resposta.json())
        .then(dados => {
          console.log("Dados recuperados do servidor:", dados);
          armazenarDadosNoCache(chave, dados);
        });
    });
}

A função obterDadosDoCache verifica se os dados solicitados estão disponíveis no cache (IndexedDB). Se os dados forem encontrados, eles são resolvidos e retornados. Se os dados não forem encontrados no cache, a função é rejeitada, e a função obterDados busca os dados do servidor usando a API fetch. Depois que os dados são recuperados do servidor, eles são armazenados no cache usando a função armazenarDadosNoCache para acesso futuro.

Ao implementar esse mecanismo de cache, você pode reduzir o número de solicitações de rede e melhorar a velocidade de carregamento da sua aplicação, servindo dados do cache local quando disponível.

Implementação de Funcionalidade de Busca

As capacidades de indexação do IndexedDB o tornam adequado para implementar funcionalidades de busca em uma aplicação web. Ao criar índices em campos pesquisáveis, você pode pesquisar e recuperar dados rapidamente com base em critérios específicos.

Exemplo: Criar índice em campo pesquisável

// Criar índice em campo pesquisável
function criarIndiceDeBusca() {
  let requisicao = indexedDB.open("MinhaBaseDeDados", 1);

  requisicao.onupgradeneeded = function(evento) {
    let db = evento.target.result;
    let objetoArmazenado = db.createObjectStore("ArmazenamentoProduto", { keyPath: "id" });
    objetoArmazenado.createIndex("nome", "nome", { unique: false });
  };
}

// Buscar dados usando índice
function buscarDados(termoBusca) {
  let transacao = db.transaction(["ArmazenamentoProduto"], "readonly");
  let objetoArmazenado = transacao.objectStore("ArmazenamentoProduto");
  let indice = objetoArmazenado.index("nome");

  let requisicao = indice.getAll(IDBKeyRange.bound(termoBusca, termoBusca + '\uffff'));

  requisicao.onsuccess = function(evento) {
    let resultados = evento.target.result;
    console.log("Resultados da busca:", resultados);
  };
}

// Exemplo de uso
criarIndiceDeBusca();

// Adicionar dados de exemplo
let transacao = db.transaction(["ArmazenamentoProduto"], "readwrite");
let objetoArmazenado = transacao.objectStore("ArmazenamentoProduto");
objetoArmazenado.add({ id: 1, nome: "Maçã", categoria: "Fruta" });
objetoArmazenado.add({ id: 2, nome: "Banana", categoria: "Fruta" });
objetoArmazenado.add({ id: 3, nome: "Laranja", categoria: "Fruta" });

// Realizar busca
buscarDados("Maç");

Criamos um índice no campo "nome" do objeto armazenado "ArmazenamentoProduto" usando a função criarIndiceDeBusca. Isso permite buscas eficientes baseadas no nome do produto.

A função buscarDados realiza uma busca usando o índice. Ela recebe um termo de busca como entrada e usa o método getAll com um IDBKeyRange para encontrar todos os produtos que correspondem ao termo de busca.

No exemplo de uso, criamos o índice de busca, adicionamos alguns dados de exemplo ao objeto armazenado "ArmazenamentoProduto" e então realizamos uma busca por produtos com "Maç" em seus nomes. Os resultados da busca são registrados no console.

Estes são apenas alguns exemplos de como o IndexedDB pode ser usado em cenários práticos. A flexibilidade e os recursos poderosos do IndexedDB o tornam adequado para uma ampla gama de casos de uso onde o armazenamento e o manuseio de dados do lado do cliente são necessários.