Resumo : neste tutorial, você aprenderá sobre o IndexedDB e como usá-lo para armazenar dados persistentemente dentro do navegador.
O que é indexadoDB
IndexedDB é um armazenamento de objetos em grande escala integrado ao navegador.
O IndexedDB permite armazenar dados de forma persistente usando pares de valores-chave.
Os valores podem ser qualquer tipo de JavaScript , incluindo boolean , number , string , undefined , null, date, object , array , regex , blob e arquivos.
Por que indexadoDB
IndexedDB permite criar aplicativos da web que podem funcionar tanto online quanto offline.
É útil para aplicativos que armazenam uma grande quantidade de dados e não precisam de uma conexão persistente com a Internet.
Por exemplo, o Google Docs usa IndexedDB para armazenar os documentos armazenados em cache no navegador e sincronizar com o servidor de vez em quando. Isso permite que o Google Docs aumente o desempenho e melhore a experiência do usuário.
E você encontrará outros tipos de aplicativos que usam fortemente o IndexedDB, como blocos de notas online, questionários, listas de tarefas, sandboxes de código e CMS.
Estrutura IndexedDB
A imagem a seguir ilustra a estrutura do IndexedDB:
Bancos de dados
Um banco de dados é o nível mais alto de IndexedDB. Um banco de dados contém um ou mais armazenamentos de objetos.
O IndexedDB pode ter um ou mais bancos de dados. Geralmente, você criará um banco de dados por aplicativo Web.
Armazenamentos de objetos
Um armazenamento de objetos é um bucket que você pode usar para armazenar os dados e os índices associados. É conceitualmente equivalente às tabelas dos bancos de dados SQL.
Um armazenamento de objetos contém os registros armazenados como pares de valores-chave.
Índices
Os índices permitem consultar dados pelas propriedades dos objetos.
Tecnicamente, você cria índices em armazenamentos de objetos, que são chamados de armazenamentos de objetos pai.
Por exemplo, se você armazenar as informações de contato, poderá criar índices de e-mail, nome e sobrenome para poder consultar os contatos por meio dessas propriedades.
Conceitos básicos do IndexedDB
A seguir, apresentamos brevemente os conceitos básicos do IndexedDB:
1) Os bancos de dados IndexedDB armazenam pares de valores-chave
Ao contrário de localStorage e sessionStorage , os valores armazenados no IndexedDB podem ser estruturas complexas como objetos e blob.
Além disso, as chaves podem ser propriedades desses objetos ou podem ser objetos binários.
Para pesquisa e classificação rápidas, você pode criar índices que usam qualquer propriedade dos objetos.
2) IndexedDB é transacional
Cada leitura e gravação nos bancos de dados IndexedDB sempre acontece em uma transação.
O modelo transacional garante a integridade dos dados caso os usuários abram a aplicação web em duas abas/janelas ao mesmo tempo e realizem a leitura e gravação no mesmo banco de dados.
3) A API IndexedDB é principalmente assíncrona
As operações IndexedDB são assíncronas. Ele usa eventos DOM para notificá-lo quando uma operação for concluída e o resultado estiver disponível.
4) IndexedDB é um sistema NoSQL
O IndexedDB é um sistema NoSQL. Em outras palavras, não usa SQL para consultar dados. Em vez disso, utiliza a consulta que retorna um cursor. Então, você pode usar o cursor para iterar o conjunto de resultados.
5) IndexedDB segue a política de mesma origem
Uma origem é um domínio, protocolo e porta de uma URL do documento onde o código é executado. Por exemplo https://tutorials.acervolima.com
:
- domínio: tutorials.acervolima.com
- protocolo: https
- porta: 443
Os https://tutorials.acervolima.com/dom/
e https://tutorials.acervolima.com/
são da mesma origem porque possuem o mesmo domínio, protocolo e porta.
No entanto, https://tutorials.acervolima.com/
e https://tutorials.acervolima.com/
não são da mesma origem, pois possuem protocolos e portas diferentes:
https://tutorials.acervolima.com | https://tutorials.acervolima.com | |
---|---|---|
Protocolo | https | http |
Porta | 443 | 80 |
IndexedDB adere à política de mesma origem. Isso significa que cada origem possui seu próprio conjunto de bancos de dados. E uma origem não pode acessar bancos de dados de outras origens.
Operações básicas do IndexedDB
A seguir descrevemos as operações básicas nos bancos de dados IndexedDB, como
- Abrindo uma conexão com um banco de dados.
- Inserindo um objeto no armazenamento de objetos.
- Lendo dados do armazenamento de objetos.
- Usando um cursor para iterar sobre um conjunto de resultados.
- Excluindo um objeto do armazenamento de objetos.
Antes de abrir uma conexão com um banco de dados no IndexedDB, vamos primeiro criar a estrutura do projeto.
1) Crie a estrutura do projeto
Primeiro, crie uma nova pasta chamada indexeddb
pasta. Dentro da indexeddb
pasta, crie outra subpasta chamada js
.
Em segundo lugar, crie na index.html
pasta indexeddb
, app.js
na js
pasta.
Terceiro, coloque a <script>
tag vinculada ao app.js
arquivo no index.html
arquivo assim:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>IndexedDB</title>
</head>
<body>
<script src="js/app.js"></script>
</body>
</html>
Linguagem de código: HTML, XML ( xml )
No app.js, você colocará todo o código JavaScript em um IIFE .
(function () {
// all the code will be here
// ...
})();
Linguagem de código: JavaScript ( javascript )
1) Verifique se o IndexedDB é compatível
O código a seguir verifica se um navegador da web oferece suporte ao IndexedDB:
if (!window.indexedDB) {
console.log(`Your browser doesn't support IndexedDB`);
return;
}
Linguagem de código: JavaScript ( javascript )
Como a maioria dos navegadores modernos suporta IndexedDB, isso pode não ser mais necessário.
2) Abra um banco de dados
Para abrir uma conexão com um banco de dados, você usa o open()
método window.indexedDB
:
const request = indexedDB.open('CRM', 1);
Linguagem de código: JavaScript ( javascript )
O open()
método aceita dois argumentos:
- O nome do banco de dados (
CRM
) - A versão do banco de dados (
1
)
O open()
método retorna um objeto de solicitação que é uma instância da IDBOpenDBRequest
interface.
Quando você chama o open()
método, ele pode ter êxito ou falhar. Para lidar com cada caso, você pode atribuir o manipulador de eventos correspondente da seguinte forma:
request.onerror = (event) => {
console.error(`Database error: ${event.target.errorCode}`);
};
request.onsuccess = (event) => {
// add implementation here
};
Linguagem de código: JavaScript ( javascript )
3) Crie armazenamentos de objetos
Ao abrir o banco de dados pela primeira vez, o onupgradeneeded
evento será acionado.
Se você abrir o banco de dados pela segunda vez com uma versão superior à versão existente, o onupgradeneeded
evento também será acionado.
Pela primeira vez, você pode usar o onupgradeneeded
manipulador de eventos para inicializar os armazenamentos e índices de objetos.
Por exemplo, o onupgradeneeded
manipulador de eventos a seguir cria o Contacts
armazenamento de objetos e seu índice.
// create the Contacts object store and indexes
request.onupgradeneeded = (event) => {
let db = event.target.result;
// create the Contacts object store
// with auto-increment id
let store = db.createObjectStore('Contacts', {
autoIncrement: true
});
// create an index on the email property
let index = store.createIndex('email', 'email', {
unique: true
});
};
Linguagem de código: JavaScript ( javascript )
Como funciona.
- Primeiro, obtenha a
IDBDatabase
instância deevent.target.result
e atribua-a àdb
variável. - Segundo, chame o
createObjectStore()
método para criar oContacts
armazenamento de objetos com aautoincrement
chave. Isso significa que o IndexedDB irá gerar um número de incremento automático começando em um como a chave para cada novo objeto inserido noContacts
armazenamento de objetos. - Terceiro, chame o
createIndex()
método para criar um índice naemail
propriedade. Como o email é único, o índice também deve ser único. Para fazer isso, você especifica o terceiro argumento docreateIndex()
método{ unique: true }
.
4) Insira dados em armazenamentos de objetos
Depois de abrir uma conexão com o banco de dados com êxito, você poderá gerenciar os dados no onsuccess
manipulador de eventos.
Por exemplo, para adicionar um objeto a um armazenamento de objetos, siga estas etapas:
- Primeiro, abra uma nova transação.
- Segundo, obtenha um armazenamento de objetos.
- Terceiro, chame o
put()
método de armazenamento de objetos para inserir um novo registro. - Por fim, feche a conexão com o banco de dados assim que a transação for concluída.
A insertContact()
função a seguir insere um novo contato no Contacts
armazenamento de objetos:
function insertContact(db, contact) {
// create a new transaction
const txn = db.transaction('Contacts', 'readwrite');
// get the Contacts object store
const store = txn.objectStore('Contacts');
//
let query = store.put(contact);
// handle success case
query.onsuccess = function (event) {
console.log(event);
};
// handle the error case
query.onerror = function (event) {
console.log(event.target.errorCode);
}
// close the database once the
// transaction completes
txn.oncomplete = function () {
db.close();
};
}
Linguagem de código: JavaScript ( javascript )
Para criar uma nova transação, você chama o transaction()
método do IDBDatabase
objeto.
Você pode abrir uma transação em um dos dois modos: readwrite
ou readonly
. O readwrite
modo permite ler e gravar dados no banco de dados, enquanto o readonly
modo permite apenas ler dados do banco de dados.
É uma boa prática abrir uma readonly
transação se você precisar ler apenas dados de um banco de dados.
Após definir a insertContact()
função, você pode chamá-la no onsuccess
manipulador de eventos da requisição para inserir um ou mais contatos assim:
request.onsuccess = (event) => {
const db = event.target.result;
insertContact(db, {
email: '[email protected]',
firstName: 'John',
lastName: 'Doe'
});
insertContact(db, {
email: '[email protected]',
firstName: 'Jane',
lastName: 'Doe'
});
};
Linguagem de código: JavaScript ( javascript )
Agora, se você abrir o index.html
arquivo no navegador da web, o código no app.js será executado para:
- Crie o
CRM
banco de dados no IndexedDB. - Crie o
Contacts
armazenamento de objetos noCRM
banco de dados. - Insira dois registros no armazenamento de objetos.
Se você abrir o devtools no navegador da web, verá o banco de dados CRM com o Contacts
armazenamento de objetos. E no Contacts
armazenamento de objetos, você verá os dados conforme mostrado na imagem a seguir:
5) Leia os dados do armazenamento de objetos por chave
Para ler um objeto por sua chave, você usa o get()
método de armazenamento de objetos. A getContactById()
função a seguir encontra um contato por um ID:
function getContactById(db, id) {
const txn = db.transaction('Contacts', 'readonly');
const store = txn.objectStore('Contacts');
let query = store.get(id);
query.onsuccess = (event) => {
if (!event.target.result) {
console.log(`The contact with ${id} not found`);
} else {
console.table(event.target.result);
}
};
query.onerror = (event) => {
console.log(event.target.errorCode);
}
txn.oncomplete = function () {
db.close();
};
};
Linguagem de código: JavaScript ( javascript )
Quando você chama o método get() do armazenamento de objetos, ele retorna uma consulta que será executada de forma assíncrona.
Como a consulta pode ser bem-sucedida ou falhar, você precisa atribuir os manipuladores onsuccess
e onerror
para lidar com cada caso.
Se a consulta for bem-sucedida, você obterá o resultado em event.target.result. Caso contrário, você receberá um código de erro via event.target.errorCode.
O código a seguir fecha a conexão com o banco de dados assim que a transação for concluída:
txn.oncomplete = function () {
db.close();
};
Linguagem de código: JavaScript ( javascript )
Na verdade, a conexão com o banco de dados é fechada somente quando todas as transações são concluídas.
O seguinte chama o manipulador getContactById()
de onsuccess
eventos para obter o contato com id 1:
request.onsuccess = (event) => {
const db = event.target.result;
getContactById(db, 1);
};
Linguagem de código: JavaScript ( javascript )
Saída:
6) Ler dados do armazenamento de objetos por um índice
O seguinte define uma nova função chamada getContactByEmail()
que usa o índice de e-mail para consultar dados:
function getContactByEmail(db, email) {
const txn = db.transaction('Contacts', 'readonly');
const store = txn.objectStore('Contacts');
// get the index from the Object Store
const index = store.index('email');
// query by indexes
let query = index.get(email);
// return the result object on success
query.onsuccess = (event) => {
console.log(query.result); // result objects
};
query.onerror = (event) => {
console.log(event.target.errorCode);
}
// close the database connection
txn.oncomplete = function () {
db.close();
};
}
Linguagem de código: JavaScript ( javascript )
Como funciona.
- Primeiro, obtenha o objeto de índice de e-mail do
Contacts
armazenamento de objetos. - Segundo, use o índice para ler os dados chamando o
get()
método. - Terceiro, mostre o resultado no
onsuccess
manipulador de eventos da consulta.
O seguinte ilustra como usar a getContactByEmail()
função no onsuccess
manipulador de eventos:
request.onsuccess = (event) => {
const db = event.target.result;
// get contact by email
getContactByEmail(db, '[email protected]');
};
Linguagem de código: JavaScript ( javascript )
Saída:
7) Leia todos os dados de um armazenamento de objetos
A seguir mostramos como usar um cursor para ler todos os objetos do Contacts
armazenamento de objetos:
function getAllContacts(db) {
const txn = db.transaction('Contacts', "readonly");
const objectStore = txn.objectStore('Contacts');
objectStore.openCursor().onsuccess = (event) => {
let cursor = event.target.result;
if (cursor) {
let contact = cursor.value;
console.log(contact);
// continue next record
cursor.continue();
}
};
// close the database connection
txn.oncomplete = function () {
db.close();
};
}
Linguagem de código: JavaScript ( javascript )
O objectStore.openCursor()
retorna um cursor usado para iterar sobre um armazenamento de objetos.
Para iterar sobre os objetos em um armazenamento de objetos usando o cursor, você precisa atribuir um onsuccess
manipulador:
objectStore.openCursor().onsuccess = (event) => {
//...
};
Linguagem de código: JavaScript ( javascript )
O event.target.result
retorna o cursor. Para obter os dados, você usa a cursor.value
propriedade.
O cursor.continue()
método avança o cursor para a posição do próximo registro no armazenamento de objetos.
O seguinte chama o manipulador getAllContacts()
de onsuccess
eventos para mostrar todos os dados do Contacts
armazenamento de objetos:
request.onsuccess = (event) => {
const db = event.target.result;
// get all contacts
getAllContacts(db);
};
Linguagem de código: JavaScript ( javascript )
Saída:
8) Exclua um contato
Para excluir um registro do armazenamento de objetos, você usa o delete()
método de armazenamento de objetos.
A função a seguir exclui um contato pelo seu ID do Contacts
armazenamento de objetos:
function deleteContact(db, id) {
// create a new transaction
const txn = db.transaction('Contacts', 'readwrite');
// get the Contacts object store
const store = txn.objectStore('Contacts');
//
let query = store.delete(id);
// handle the success case
query.onsuccess = function (event) {
console.log(event);
};
// handle the error case
query.onerror = function (event) {
console.log(event.target.errorCode);
}
// close the database once the
// transaction completes
txn.oncomplete = function () {
db.close();
};
}
Linguagem de código: JavaScript ( javascript )
E você pode chamar a deleteContact()
função no onsuccess
manipulador de eventos para excluir o contato com id 1 da seguinte maneira:
request.onsuccess = (event) => {
const db = event.target.result;
deleteContact(db, 1);
};
Linguagem de código: JavaScript ( javascript )
Se você executar o código, verá que o contato com id 1 será excluído.
Junte tudo
O seguinte mostra o arquivo app.js completo:
(function () {
// check for IndexedDB support
if (!window.indexedDB) {
console.log(`Your browser doesn't support IndexedDB`);
return;
}
// open the CRM database with the version 1
const request = indexedDB.open('CRM', 1);
// create the Contacts object store and indexes
request.onupgradeneeded = (event) => {
let db = event.target.result;
// create the Contacts object store
// with auto-increment id
let store = db.createObjectStore('Contacts', {
autoIncrement: true
});
// create an index on the email property
let index = store.createIndex('email', 'email', {
unique: true
});
};
// handle the error event
request.onerror = (event) => {
console.error(`Database error: ${event.target.errorCode}`);
};
// handle the success event
request.onsuccess = (event) => {
const db = event.target.result;
// insert contacts
// insertContact(db, {
// email: '[email protected]',
// firstName: 'John',
// lastName: 'Doe'
// });
// insertContact(db, {
// email: '[email protected]',
// firstName: 'Jane',
// lastName: 'Doe'
// });
// get contact by id 1
// getContactById(db, 1);
// get contact by email
// getContactByEmail(db, '[email protected]');
// get all contacts
// getAllContacts(db);
deleteContact(db, 1);
};
function insertContact(db, contact) {
// create a new transaction
const txn = db.transaction('Contacts', 'readwrite');
// get the Contacts object store
const store = txn.objectStore('Contacts');
//
let query = store.put(contact);
// handle success case
query.onsuccess = function (event) {
console.log(event);
};
// handle the error case
query.onerror = function (event) {
console.log(event.target.errorCode);
}
// close the database once the
// transaction completes
txn.oncomplete = function () {
db.close();
};
}
function getContactById(db, id) {
const txn = db.transaction('Contacts', 'readonly');
const store = txn.objectStore('Contacts');
let query = store.get(id);
query.onsuccess = (event) => {
if (!event.target.result) {
console.log(`The contact with ${id} not found`);
} else {
console.table(event.target.result);
}
};
query.onerror = (event) => {
console.log(event.target.errorCode);
}
txn.oncomplete = function () {
db.close();
};
};
function getContactByEmail(db, email) {
const txn = db.transaction('Contacts', 'readonly');
const store = txn.objectStore('Contacts');
// get the index from the Object Store
const index = store.index('email');
// query by indexes
let query = index.get(email);
// return the result object on success
query.onsuccess = (event) => {
console.table(query.result); // result objects
};
query.onerror = (event) => {
console.log(event.target.errorCode);
}
// close the database connection
txn.oncomplete = function () {
db.close();
};
}
function getAllContacts(db) {
const txn = db.transaction('Contacts', "readonly");
const objectStore = txn.objectStore('Contacts');
objectStore.openCursor().onsuccess = (event) => {
let cursor = event.target.result;
if (cursor) {
let contact = cursor.value;
console.log(contact);
// continue next record
cursor.continue();
}
};
// close the database connection
txn.oncomplete = function () {
db.close();
};
}
function deleteContact(db, id) {
// create a new transaction
const txn = db.transaction('Contacts', 'readwrite');
// get the Contacts object store
const store = txn.objectStore('Contacts');
//
let query = store.delete(id);
// handle the success case
query.onsuccess = function (event) {
console.log(event);
};
// handle the error case
query.onerror = function (event) {
console.log(event.target.errorCode);
}
// close the database once the
// transaction completes
txn.oncomplete = function () {
db.close();
};
}
})();
Linguagem de código: JavaScript ( javascript )
Resumo
- O IndexedDB é um objeto de grande escala armazenado em navegadores da web.
- O IndexedDB armazena dados como pares de valores-chave. Os valores podem ser quaisquer dados, incluindo dados simples e complexos.
- O IndexedDB consiste em um ou mais bancos de dados. Cada banco de dados possui um ou mais armazenamentos de objetos. Normalmente, você cria um banco de dados no IndexedDB por aplicativo da web.
- O IndexedDB é útil para aplicações web que não requerem uma conexão persistente com a Internet, especialmente para aplicações que funcionam tanto online quanto offline.