Este guia aborda os conceitos básicos da
API IndexedDB.
Estamos usando o projeto
IndexedDB promised (em inglês)
semelhante à API IndexedDB, mas que usa promessas, que
você pode await
para uma sintaxe mais concisa. Isso simplifica a API e, ao mesmo tempo,
para manter a estrutura.
O que é IndexedDB?
O IndexedDB é um sistema de armazenamento NoSQL em grande escala que permite o armazenamento de apenas sobre qualquer coisa no navegador do usuário. Além dos métodos usuais de pesquisa, get e put ações, o IndexedDB também oferece suporte a transações e é adequado para armazenar grandes quantidades de dados estruturados.
Cada banco de dados IndexedDB é exclusivo para um objeto origin (normalmente o domínio ou subdomínio do site), o que significa que ele não pode acessar ou ser acessado por qualquer outra origem. Os limites de armazenamento de dados são geralmente grandes, se existirem, mas navegadores diferentes lidam com os limites e remoção de dados de maneiras diferentes. Consulte a seção Leitura adicional para mais informações.
Termos do IndexedDB
- banco de dados
- O nível mais alto do IndexedDB. Ele contém os armazenamentos de objetos, que, por sua vez, contêm os dados que você quer persistir. É possível criar vários bancos de dados com os nomes que você escolher.
- Armazenamento de objetos
- Um bucket individual para armazenar dados, semelhante às tabelas em bancos de dados relacionais.
Normalmente, há um armazenamento de objetos para cada tipo (não dados JavaScript).
tipo) dos dados armazenados. Ao contrário das tabelas de banco de dados, os dados JavaScript
tipos de dados em um repositório não precisam ser consistentes. Por exemplo, se um app
tem um armazenamento de objetos
people
contendo informações sobre três pessoas, aquelas as propriedades de idade das pessoas poderiam ser53
,'twenty-five'
eunknown
. - Índice
- Um tipo de armazenamento de objeto para organizar dados em outro armazenamento de objetos (chamado de armazenamento de objetos de referência) por uma propriedade individual dos dados. O índice é usado para recuperar registros no armazenamento de objetos por esta propriedade. Por exemplo, se você estiver armazenar pessoas, convém buscá-las mais tarde por seu nome, idade, ou animal favorito.
- Operação
- Uma interação com o banco de dados.
- Transação
- Um wrapper em torno de uma operação ou grupo de operações que garante que o banco de dados integridade dos dados. Se uma das ações em uma transação falhar, nenhuma delas será é aplicada e o banco de dados retorna ao estado em que estava antes da transação começou. Todas as operações de leitura ou gravação no IndexedDB precisam fazer parte de uma transação. Isso permite operações atômicas de leitura-modificação-gravação sem o risco de conflitos com outras linhas de execução atuando no banco de dados ao mesmo tempo.
- Cursor
- Um mecanismo para iterar vários registros em um banco de dados.
Como verificar o suporte a IndexedDB
O IndexedDB tem suporte universal.
Entretanto, se você estiver trabalhando com navegadores mais antigos, não é má ideia
suporte à detecção de recursos, por precaução. A maneira mais fácil é conferir o window
objeto:
function indexedDBStuff () {
// Check for IndexedDB support:
if (!('indexedDB' in window)) {
// Can't use IndexedDB
console.log("This browser doesn't support IndexedDB");
return;
} else {
// Do IndexedDB stuff here:
// ...
}
}
// Run IndexedDB code:
indexedDBStuff();
Como abrir um banco de dados
Com o IndexedDB, você pode criar vários bancos de dados com qualquer nome que quiser. Se
um banco de dados não existe quando você tenta abri-lo, mas não criadas automaticamente.
Para abrir um banco de dados, use o método openDB()
da biblioteca idb
:
import {openDB} from 'idb';
async function useDB () {
// Returns a promise, which makes `idb` usable with async-await.
const dbPromise = await openDB('example-database', version, events);
}
useDB();
Esse método retorna uma promessa que é resolvida em um objeto de banco de dados. Ao usar o botão
openDB()
, forneça um nome, um número de versão e um objeto de eventos para definir
o banco de dados.
Confira um exemplo do método openDB()
no contexto:
import {openDB} from 'idb';
async function useDB () {
// Opens the first version of the 'test-db1' database.
// If the database does not exist, it will be created.
const dbPromise = await openDB('test-db1', 1);
}
useDB();
Coloque a verificação do suporte a IndexedDB na parte superior da função anônima. Isso
sai da função se o navegador não oferece suporte a IndexedDB. Se a função puder
continuar, ele chamará o método openDB()
para abrir um banco de dados chamado 'test-db1'
.
Neste exemplo, o objeto de eventos opcionais foi deixado de fora para manter as coisas
simples, mas você precisa especificá-lo para fazer qualquer trabalho significativo com IndexedDB.
Como trabalhar com armazenamentos de objetos
Um banco de dados do IndexedDB contém um ou mais armazenamentos de objetos, cada um com uma coluna para uma chave e outra para os dados associados a essa chave.
Criar armazenamentos de objetos
Um banco de dados IndexedDB bem estruturado precisa ter um repositório de objetos para cada tipo
de dados que precisam ser persistidos. Por exemplo, um site que persiste ao usuário
os perfis e as observações podem ter um armazenamento de objetos people
contendo person
e um armazenamento de objetos notes
que contém objetos note
.
Para garantir a integridade do banco de dados, você só pode criar ou remover armazenamentos de objetos no
objeto de eventos em uma chamada openDB()
. O objeto de eventos expõe um objeto upgrade()
que permite criar armazenamentos de objetos. Chame o método
createObjectStore()
dentro do método upgrade()
para criar o armazenamento de objetos:
import {openDB} from 'idb';
async function createStoreInDB () {
const dbPromise = await openDB('example-database', 1, {
upgrade (db) {
// Creates an object store:
db.createObjectStore('storeName', options);
}
});
}
createStoreInDB();
Esse método recebe o nome do armazenamento de objetos e uma configuração opcional que permite definir várias propriedades para o armazenamento de objetos.
Confira abaixo um exemplo de como usar createObjectStore()
:
import {openDB} from 'idb';
async function createStoreInDB () {
const dbPromise = await openDB('test-db1', 1, {
upgrade (db) {
console.log('Creating a new object store...');
// Checks if the object store exists:
if (!db.objectStoreNames.contains('people')) {
// If the object store does not exist, create it:
db.createObjectStore('people');
}
}
});
}
createStoreInDB();
Neste exemplo, um objeto de eventos é transmitido ao método openDB()
para criar
repositório de objetos e, como antes, o trabalho de criação do armazenamento de objetos está concluído
no método upgrade()
do objeto de evento. No entanto, como o navegador gera uma
se você tentar criar um armazenamento de objetos que já existe, recomendamos
encapsulando o método createObjectStore()
em uma instrução if
que verifica
se o armazenamento de objetos existe. No bloco if
, chame
createObjectStore()
para criar um repositório de objetos chamado 'firstOS'
.
Como definir chaves primárias
Ao definir armazenamentos de objetos, é possível definir como os dados são identificados exclusivamente em armazenamento usando uma chave primária. É possível definir uma chave primária definindo uma ou usando um gerador de chaves.
Um caminho de chave é uma propriedade que sempre existe e contém um valor exclusivo. Para
exemplo, no caso de um armazenamento de objetos people
, é possível escolher o e-mail
como o caminho da chave:
import {openDB} from 'idb';
async function createStoreInDB () {
const dbPromise = await openDB('test-db2', 1, {
upgrade (db) {
if (!db.objectStoreNames.contains('people')) {
db.createObjectStore('people', { keyPath: 'email' });
}
}
});
}
createStoreInDB();
Este exemplo cria um armazenamento de objetos chamado 'people'
e atribui o email
como a chave primária na opção keyPath
.
Você também pode usar um gerador de chaves, como autoIncrement
. Gerador de chaves
cria um valor exclusivo para cada objeto adicionado ao armazenamento de objetos. Por padrão,
Se você não especificar uma chave, o IndexedDB criará uma chave e a armazenará separadamente.
dos dados.
O exemplo a seguir cria um armazenamento de objetos chamado 'notes'
e define o
chave primária seja atribuída automaticamente como um número de incremento automático:
import {openDB} from 'idb';
async function createStoreInDB () {
const dbPromise = await openDB('test-db2', 1, {
upgrade (db) {
if (!db.objectStoreNames.contains('notes')) {
db.createObjectStore('notes', { autoIncrement: true });
}
}
});
}
createStoreInDB();
O exemplo a seguir é semelhante ao anterior, mas, desta vez,
o valor de incremento automático foi atribuído explicitamente a uma propriedade chamada 'id'
.
import {openDB} from 'idb';
async function createStoreInDB () {
const dbPromise = await openDB('test-db2', 1, {
upgrade (db) {
if (!db.objectStoreNames.contains('logs')) {
db.createObjectStore('logs', { keyPath: 'id', autoIncrement: true });
}
}
});
}
createStoreInDB();
A escolha do método para definir a chave depende dos seus dados. Se as
dados têm uma propriedade que é sempre única, você pode torná-lo o keyPath
para
impor essa exclusividade. Caso contrário, use um valor de incremento automático.
O código a seguir cria três armazenamentos de objetos que demonstram as várias formas de definir chaves primárias em armazenamentos de objetos:
import {openDB} from 'idb';
async function createStoresInDB () {
const dbPromise = await openDB('test-db2', 1, {
upgrade (db) {
if (!db.objectStoreNames.contains('people')) {
db.createObjectStore('people', { keyPath: 'email' });
}
if (!db.objectStoreNames.contains('notes')) {
db.createObjectStore('notes', { autoIncrement: true });
}
if (!db.objectStoreNames.contains('logs')) {
db.createObjectStore('logs', { keyPath: 'id', autoIncrement: true });
}
}
});
}
createStoresInDB();
Como definir índices
Índices são um tipo de armazenamento de objetos usado para recuperar dados da referência por uma propriedade especificada. Um índice reside dentro do objeto de referência armazenam e contém os mesmos dados, mas usa a propriedade especificada como seu em vez da chave primária do armazenamento de referência. Os índices devem ser feitos quando você cria seus armazenamentos de objetos e pode ser usado para definir uma restrição única sobre seus dados.
Para criar um índice, chame o método createIndex()
em uma instância de repositório de objetos:
import {openDB} from 'idb';
async function createIndexInStore() {
const dbPromise = await openDB('storeName', 1, {
upgrade (db) {
const objectStore = db.createObjectStore('storeName');
objectStore.createIndex('indexName', 'property', options);
}
});
}
createIndexInStore();
Este método cria e retorna um objeto de índice. O método createIndex()
no
a instância do repositório de objetos toma o nome do novo índice como a primeira
e o segundo argumento se refere à propriedade nos dados que você deseja
índice. O argumento final permite definir duas opções que determinam como
o índice opera: unique
e multiEntry
. Se unique
estiver definido como true
, o
índice não permite valores duplicados para uma única chave. A seguir, multiEntry
determina como createIndex()
se comporta quando a propriedade indexada é uma matriz. Se
ele estiver definido como true
, createIndex()
adicionará uma entrada no índice para cada matriz
. Caso contrário, ele adiciona uma única entrada contendo a matriz.
Veja um exemplo:
import {openDB} from 'idb';
async function createIndexesInStores () {
const dbPromise = await openDB('test-db3', 1, {
upgrade (db) {
if (!db.objectStoreNames.contains('people')) {
const peopleObjectStore = db.createObjectStore('people', { keyPath: 'email' });
peopleObjectStore.createIndex('gender', 'gender', { unique: false });
peopleObjectStore.createIndex('ssn', 'ssn', { unique: true });
}
if (!db.objectStoreNames.contains('notes')) {
const notesObjectStore = db.createObjectStore('notes', { autoIncrement: true });
notesObjectStore.createIndex('title', 'title', { unique: false });
}
if (!db.objectStoreNames.contains('logs')) {
const logsObjectStore = db.createObjectStore('logs', { keyPath: 'id', autoIncrement: true });
}
}
});
}
createIndexesInStores();
Neste exemplo, os armazenamentos de objetos 'people'
e 'notes'
têm índices. Para
crie os índices, primeiro atribua o resultado de createObjectStore()
(um objeto
store) a uma variável para chamar createIndex()
nela.
Como trabalhar com dados
Esta seção descreve como criar, ler, atualizar e excluir dados. Esses
as operações são todas assíncronas, usando promessas em que a API IndexedDB usa
solicitações. Isso simplifica a API. Em vez de detectar eventos acionados por
solicitação, é possível chamar .then()
no objeto de banco de dados retornado do
método openDB()
para iniciar interações com o banco de dados, ou await
seu
criação.
Todas as operações de dados no IndexedDB são realizadas em uma transação. Cada tem o seguinte formato:
- Recebe o objeto do banco de dados.
- Abrir transação no banco de dados.
- Abre o armazenamento do objeto na transação.
- Realizar a operação no armazenamento de objetos.
Uma transação pode ser considerada um wrapper seguro em torno de uma operação ou um grupo. das operações. Se uma das ações em uma transação falhar, todas as ações são revertidas. As transações são específicas a um ou mais armazenamentos de objetos, que você define ao abrir a transação. Eles podem ser somente leitura ou somente leitura e escrever. Isso indica se as operações dentro da transação leram dados ou fazer alterações no banco de dados.
Criar dados
Para criar dados, chame o método add()
na instância do banco de dados e passar os dados que deseja adicionar. O add()
o primeiro argumento do método é o armazenamento de objetos ao qual deseja adicionar os dados e o
O segundo argumento é um objeto que contém os campos e dados associados que você deseja
adicionar. Este é o exemplo mais simples, em que uma única linha de dados é adicionada:
import {openDB} from 'idb';
async function addItemToStore () {
const db = await openDB('example-database', 1);
await db.add('storeName', {
field: 'data'
});
}
addItemToStore();
Cada chamada add()
acontece dentro de uma transação, ou seja, mesmo que a promessa seja resolvida
com sucesso, isso não significa necessariamente que a operação funcionou. Para ter certeza
depois que a operação de adição for realizada, verifique se toda
transação foi concluída usando o método transaction.done()
. Esta é uma
que é resolvida quando a transação é concluída e é rejeitada se o
erros de transação. É preciso verificar todas as "gravações" operações
porque essa é a única maneira de saber que as alterações no banco de dados
acontecido.
O código a seguir mostra o uso do método add()
dentro de uma transação:
import {openDB} from 'idb';
async function addItemsToStore () {
const db = await openDB('test-db4', 1, {
upgrade (db) {
if (!db.objectStoreNames.contains('foods')) {
db.createObjectStore('foods', { keyPath: 'name' });
}
}
});
// Create a transaction on the 'foods' store in read/write mode:
const tx = db.transaction('foods', 'readwrite');
// Add multiple items to the 'foods' store in a single transaction:
await Promise.all([
tx.store.add({
name: 'Sandwich',
price: 4.99,
description: 'A very tasty sandwich!',
created: new Date().getTime(),
}),
tx.store.add({
name: 'Eggs',
price: 2.99,
description: 'Some nice eggs you can cook up!',
created: new Date().getTime(),
}),
tx.done
]);
}
addItemsToStore();
Depois de abrir o banco de dados (e criar um armazenamento de objetos, se necessário), você precisará
para abrir uma transação chamando o método transaction()
nela. Esse método
usa um argumento para a loja em que você quer fazer transações, bem como o modo.
Neste caso, queremos escrever para a loja, então este exemplo
especifica 'readwrite'
.
A próxima etapa é começar a adicionar itens à loja como parte da transação.
No exemplo anterior, estamos lidando com três operações no 'foods'
store que retornem uma promessa:
- Adicionando um registro de um sanduíche saboroso.
- Adicionando um registro para alguns ovos.
- Sinalizando que a transação foi concluída (
tx.done
).
Como todas essas ações são baseadas em promessas, precisamos esperar que
eles terminarem. Passar essas promessas para
Promise.all
é uma maneira boa e ergonômica de fazer isso. Promise.all
aceita uma matriz de
promessas e termina quando todas as promessas passadas a ele são resolvidas.
Para os dois registros que estão sendo adicionados, a interface store
da instância da transação
chama add()
e transmite os dados para ele. Você pode await
a chamada Promise.all
para que ela seja finalizada quando a transação for concluída.
Ler dados
Para ler os dados, chame o método get()
na instância do banco de dados que você recupera usando o método openDB()
.
get()
usa o nome do repositório e o valor da chave primária do objeto que você
recuperar. Este é um exemplo básico:
import {openDB} from 'idb';
async function getItemFromStore () {
const db = await openDB('example-database', 1);
// Get a value from the object store by its primary key value:
const value = await db.get('storeName', 'unique-primary-key-value');
}
getItemFromStore();
Assim como acontece com add()
, o método get()
retorna uma promessa. Portanto, você pode await
se
de sua preferência ou use o callback .then()
da promessa.
O exemplo a seguir usa o método get()
no banco de dados 'test-db4'
Armazenamento do objeto 'foods'
para receber uma única linha pela chave primária 'name'
:
import {openDB} from 'idb';
async function getItemFromStore () {
const db = await openDB('test-db4', 1);
const value = await db.get('foods', 'Sandwich');
console.dir(value);
}
getItemFromStore();
Recuperar uma única linha do banco de dados é bem simples: abrir
banco de dados e especificar o armazenamento de objetos e o valor da chave primária da linha que você
que você quer extrair dados. Como o método get()
retorna uma promessa, é possível
await
.
Atualizar dados
Para atualizar os dados, chame o método put()
no armazenamento de objetos. O método put()
é semelhante ao método add()
.
e também pode ser usado no lugar de add()
para criar dados. Aqui está um exemplo básico
de usar put()
para atualizar uma linha em um armazenamento de objetos pelo valor da chave primária:
import {openDB} from 'idb';
async function updateItemInStore () {
const db = await openDB('example-database', 1);
// Update a value from in an object store with an inline key:
await db.put('storeName', { inlineKeyName: 'newValue' });
// Update a value from in an object store with an out-of-line key.
// In this case, the out-of-line key value is 1, which is the
// auto-incremented value.
await db.put('otherStoreName', { field: 'value' }, 1);
}
updateItemInStore();
Assim como outros métodos, esse método retorna uma promessa. Você também pode usar put()
como
parte de uma transação. Confira um exemplo que usa a loja 'foods'
anterior.
que atualiza o preço do sanduíche e dos ovos:
import {openDB} from 'idb';
async function updateItemsInStore () {
const db = await openDB('test-db4', 1);
// Create a transaction on the 'foods' store in read/write mode:
const tx = db.transaction('foods', 'readwrite');
// Update multiple items in the 'foods' store in a single transaction:
await Promise.all([
tx.store.put({
name: 'Sandwich',
price: 5.99,
description: 'A MORE tasty sandwich!',
updated: new Date().getTime() // This creates a new field
}),
tx.store.put({
name: 'Eggs',
price: 3.99,
description: 'Some even NICER eggs you can cook up!',
updated: new Date().getTime() // This creates a new field
}),
tx.done
]);
}
updateItemsInStore();
A maneira como os itens são atualizados depende de como você define uma chave. Se você definir um keyPath
,
cada linha no armazenamento de objetos está associada a uma chave inline. As regras
atualiza linhas com base nessa chave, e quando você atualiza linhas neste
situação, é preciso especificar essa chave para atualizar o item apropriado no
armazenamento de objetos. Também é possível criar uma chave fora de linha configurando uma
autoIncrement
como a chave primária.
Excluir dados
Para excluir dados, chame o método delete()
no armazenamento de objetos:
import {openDB} from 'idb';
async function deleteItemFromStore () {
const db = await openDB('example-database', 1);
// Delete a value
await db.delete('storeName', 'primary-key-value');
}
deleteItemFromStore();
Assim como add()
e put()
, é possível usar isso como parte de uma transação:
import {openDB} from 'idb';
async function deleteItemsFromStore () {
const db = await openDB('test-db4', 1);
// Create a transaction on the 'foods' store in read/write mode:
const tx = db.transaction('foods', 'readwrite');
// Delete multiple items from the 'foods' store in a single transaction:
await Promise.all([
tx.store.delete('Sandwich'),
tx.store.delete('Eggs'),
tx.done
]);
}
deleteItemsFromStore();
A estrutura da interação com o banco de dados é a mesma das outras
as operações. Lembre-se de verificar se toda a transação foi concluída até
incluindo o método tx.done
na matriz que você transmite para Promise.all
.
Como extrair todos os dados
Até agora, você só recuperou um objeto do armazenamento de cada vez. Você também pode
recuperar todos os dados, ou um subconjunto, de um repositório ou índice de objetos usando
o método getAll()
ou os cursores.
Método getAll()
A maneira mais simples de extrair todos os dados de um armazenamento de objetos é chamar getAll()
no repositório ou índice do objeto, da seguinte forma:
import {openDB} from 'idb';
async function getAllItemsFromStore () {
const db = await openDB('test-db4', 1);
// Get all values from the designated object store:
const allValues = await db.getAll('storeName');
console.dir(allValues);
}
getAllItemsFromStore();
Esse método retorna todos os objetos no armazenamento de objetos, sem restrições seja qual for o caso. É a maneira mais direta de obter todos os valores de um armazenamento de objetos, mas também a menos flexível.
import {openDB} from 'idb';
async function getAllItemsFromStore () {
const db = await openDB('test-db4', 1);
// Get all values from the designated object store:
const allValues = await db.getAll('foods');
console.dir(allValues);
}
getAllItemsFromStore();
Este exemplo chama getAll()
no armazenamento de objetos 'foods'
. Isso retorna todos
os objetos de 'foods'
, ordenados pela chave primária.
Como usar cursores
Os cursores são uma maneira mais flexível de recuperar vários objetos. Um cursor seleciona um a um, permitindo que você faça algo com os dados quando são selecionados. Cursores, assim como as outras operações de banco de dados, a trabalhar em transações.
Para criar um cursor, chame openCursor()
.
no armazenamento de objetos como parte de uma transação. Usando a loja 'foods'
de
dos exemplos anteriores, isso é como avançar um cursor por todas as linhas de dados na
um repositório de objetos:
import {openDB} from 'idb';
async function getAllItemsFromStoreWithCursor () {
const db = await openDB('test-db4', 1);
const tx = await db.transaction('foods', 'readonly');
// Open a cursor on the designated object store:
let cursor = await tx.store.openCursor();
// Iterate on the cursor, row by row:
while (cursor) {
// Show the data in the row at the current cursor position:
console.log(cursor.key, cursor.value);
// Advance the cursor to the next row:
cursor = await cursor.continue();
}
}
getAllItemsFromStoreWithCursor();
Nesse caso, a transação é aberta no modo 'readonly'
, e
o método openCursor
seja chamado. Em uma repetição while
subsequente, a linha no
a posição atual do cursor pode ter as propriedades key
e value
lidas, e
é possível operar nesses valores da maneira que fizer mais sentido para seu
app. Quando estiver tudo pronto, chame o método continue()
do objeto cursor
.
para ir para a próxima linha, e a repetição while
termina quando o cursor
chega ao final do conjunto de dados.
Usar cursores com intervalos e índices
Os índices permitem buscar os dados em um armazenamento de objetos por uma propriedade diferente da
chave primária. É possível criar um índice em qualquer propriedade, que se torna o keyPath
para o índice, especifique um intervalo nessa propriedade e obtenha os dados dentro da
usando getAll()
ou um cursor.
Defina o intervalo usando o objeto IDBKeyRange
. e qualquer um dos seguintes
métodos:
upperBound()
.lowerBound()
.bound()
(ambos).only()
.includes()
.
Os métodos upperBound()
e lowerBound()
especificam os limites máximos e mínimos
do intervalo.
IDBKeyRange.lowerBound(indexKey);
ou:
IDBKeyRange.upperBound(indexKey);
Cada uma usa um argumento: o valor keyPath
do índice para o item que você quer.
que será especificado como o limite máximo ou mínimo.
O método bound()
especifica os limites máximo e mínimo:
IDBKeyRange.bound(lowerIndexKey, upperIndexKey);
O intervalo dessas funções é inclusivo por padrão, ou seja, inclui
os dados que é especificado como os limites do intervalo. Para deixar de fora esses valores,
especifique o intervalo como exclusivo, passando true
como o segundo argumento para
lowerBound()
ou upperBound()
, ou como o terceiro e quarto argumentos de
bound()
para os limites mínimo e máximo, respectivamente.
O próximo exemplo usa um índice na propriedade 'price'
no objeto 'foods'
loja on-line. A loja agora também tem um formulário anexado a ela com duas entradas para o
limites máximos e mínimos do intervalo. Use o código a seguir para encontrar alimentos com
os preços entre esses limites:
import {openDB} from 'idb';
async function searchItems (lower, upper) {
if (!lower === '' && upper === '') {
return;
}
let range;
if (lower !== '' && upper !== '') {
range = IDBKeyRange.bound(lower, upper);
} else if (lower === '') {
range = IDBKeyRange.upperBound(upper);
} else {
range = IDBKeyRange.lowerBound(lower);
}
const db = await openDB('test-db4', 1);
const tx = await db.transaction('foods', 'readonly');
const index = tx.store.index('price');
// Open a cursor on the designated object store:
let cursor = await index.openCursor(range);
if (!cursor) {
return;
}
// Iterate on the cursor, row by row:
while (cursor) {
// Show the data in the row at the current cursor position:
console.log(cursor.key, cursor.value);
// Advance the cursor to the next row:
cursor = await cursor.continue();
}
}
// Get items priced between one and four dollars:
searchItems(1.00, 4.00);
O código de exemplo primeiro recebe os valores dos limites e verifica se eles
existem. O próximo bloco de código decide qual método usar para limitar o intervalo
com base nos valores. Na interação com o banco de dados, abra o armazenamento do objeto na
transação como de costume, depois abra o índice 'price'
no armazenamento de objetos. A
Com o índice 'price'
, você pode pesquisar itens por preço.
Em seguida, o código abre um cursor no índice e passa o intervalo. O cursor
retorna uma promessa que representa o primeiro objeto no intervalo, ou undefined
se
não há dados dentro do intervalo. O método cursor.continue()
retorna uma
cursor que representa o próximo objeto e continua pelo loop até que você
chegar ao fim do intervalo.
Controle de versão do banco de dados
Ao chamar o método openDB()
, especifique o número da versão do banco de dados
no segundo parâmetro. Em todos os exemplos neste guia, a versão foi
definida como 1
, mas é possível fazer upgrade de um banco de dados para uma nova versão caso seja necessário
modificá-lo de alguma forma. Se a versão especificada for superior à versão do
o banco de dados atual, o callback upgrade
no objeto de evento é executado,
o que permite adicionar novos armazenamentos de objetos e índices ao banco de dados.
O objeto db
no callback upgrade
tem uma propriedade oldVersion
especial,
que indica o número da versão do banco de dados ao qual o navegador tem acesso.
Você pode transmitir esse número de versão para uma instrução switch
para executar blocos de
dentro do callback upgrade
com base na versão do banco de dados
número Veja um exemplo:
import {openDB} from 'idb';
const db = await openDB('example-database', 2, {
upgrade (db, oldVersion) {
switch (oldVersion) {
case 0:
// Create first object store:
db.createObjectStore('store', { keyPath: 'name' });
case 1:
// Get the original object store, and create an index on it:
const tx = await db.transaction('store', 'readwrite');
tx.store.createIndex('name', 'name');
}
}
});
Neste exemplo, a versão mais recente do banco de dados é definida como 2
. Quando esse código
é executado pela primeira vez, o banco de dados ainda não existe no navegador, então oldVersion
é 0
, e a instrução switch
começa em case 0
. No exemplo,
adiciona um armazenamento de objetos 'store'
ao banco de dados.
Ponto-chave: em instruções switch
, geralmente há um break
após cada case
.
mas isso não é usado deliberadamente aqui. Dessa forma, se o processo
o banco de dados está com algumas versões atrasadas ou, se ele não existir, o código continuará
pelo restante dos blocos case
até que ele esteja atualizado. Então, no exemplo,
o navegador continua sendo executado por case 1
, criando um índice name
no
Armazenamento do objeto store
.
Para criar um índice 'description'
no armazenamento de objetos 'store'
, atualize o
número da versão e adicione um novo bloco case
da seguinte maneira:
import {openDB} from 'idb';
const db = await openDB('example-database', 3, {
upgrade (db, oldVersion) {
switch (oldVersion) {
case 0:
// Create first object store:
db.createObjectStore('store', { keyPath: 'name' });
case 1:
// Get the original object store, and create an index on it:
const tx = await db.transaction('store', 'readwrite');
tx.store.createIndex('name', 'name');
case 2:
const tx = await db.transaction('store', 'readwrite');
tx.store.createIndex('description', 'description');
}
}
});
Se o banco de dados que você criou no exemplo anterior ainda existir no navegador,
quando for executado, oldVersion
será 2
. O navegador pula case 0
e
case 1
e executa o código em case 2
, que cria um description
índice. Depois disso, o navegador tem um banco de dados na versão 3, contendo uma store
armazenamento de objetos com índices name
e description
.
Leitura adicional
Os recursos a seguir fornecem mais informações e contexto para o uso do IndexedDB.
Documentação do IndexedDB
- Repositório
idb
do GitHub - Como usar o IndexedDB
- Conceitos básicos do IndexedDB
- Especificação da API Indexed Database 3.0