索引資料庫基本概念指南。
本指南說明 IndexedDB API 的基本概念。我們使用 Jake Archibald 的 IndexedDB Promised 程式庫,這個程式庫與 IndexedDB API 非常類似,但使用了 promise (語法更簡潔的語法,您可以 await
)。這可以簡化 API,同時維持 API 的結構。
什麼是 IndexedDB?
IndexedDB 是大規模的 NoSQL 儲存系統,可存放使用者瀏覽器中的任何內容。除了一般的搜尋、取得和放置動作以外,IndexedDB 也支援交易。MDN 上 IndexedDB 的定義:
IndexedDB 是一種低階 API,適合用於用戶端儲存大量結構化資料 (包括檔案/blob)。這個 API 會使用索引,以便針對這項資料執行高效能搜尋。雖然 DOM 儲存空間適合用來儲存少量資料,但較不適合儲存大量結構化資料。IndexedDB 提供解決方案。
每個 IndexedDB 資料庫都具有專屬的「來源」 (通常是網站網域或子網域),也就是說,其他來源無法存取或存取該資料庫。資料儲存空間上限通常都十分龐大,如果確實有這類限制,但各個瀏覽器處理限制和資料清除的方式不同。詳情請參閱延伸閱讀一節。
IndexedDB 字詞
- 資料庫
- IndexedDB 的最高等級。其中包含物件儲存庫,以及您要保存的資料。您可以使用任何名稱建立多個資料庫。
- 物件存放區
- 用來儲存資料的個別值區。您可以將物件儲存庫視為傳統關聯資料庫中的資料表。一般而言,儲存的資料的每種「類型」 (非 JavaScript 資料類型) 都有一個物件儲存區。舉例來說,假設某個應用程式持續顯示網誌文章和使用者個人資料,那麼你可以將兩個物件儲存起來。與傳統資料庫中的資料表不同,商店內實際的 JavaScript 資料類型不必一致 (例如,如果
people
物件儲存庫中有三名使用者,其年齡屬性可能是53
、'twenty-five'
和unknown
)。 - 索引
- 一種物件儲存庫,可透過資料的個別屬性整理其他物件儲存庫 (稱為參照物件儲存庫) 中的資料。索引可以由這個屬性擷取物件儲存庫中的記錄。舉例來說,如要儲存使用者,建議日後再輸入使用者名稱、年齡或喜愛的動物。
- 作業
- 與資料庫的互動。
- 交易
- 這是一個作業 (或一組作業) 的包裝函式,可確保資料庫完整性。如果交易中的其中一個動作失敗,系統不會套用這些動作,且資料庫會返回交易開始前的狀態。IndexedDB 中的所有讀取或寫入作業都必須屬於交易的一部分。這可讓您執行不可部分完成的讀取-修改-寫入作業,而不必擔心會同時在資料庫上執行其他執行緒。
- 遊標
- 疊代資料庫中多筆記錄的機制。
如何檢查 IndexedDB 支援
索引資料庫幾乎普遍支援索引資料庫。不過,如果您使用的是舊版瀏覽器,就不建議使用功能偵測支援功能。最簡單的方法是檢查 window
物件:
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();
如何開啟資料庫
使用 IndexedDB 時,您就可以使用任意名稱建立多個資料庫。嘗試開啟資料庫時,如果資料庫不存在,系統會自動建立該資料庫。如要開啟資料庫,您可以使用 openDB()
方法搭配 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();
這個方法會傳回一個承諾,這個會解析為資料庫物件。使用 openDB()
時,您需要提供名稱、版本編號和事件物件來設定資料庫。
以下是結構定義中的 openDB()
方法範例:
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();
請將 IndexedDB 支援檢查置於匿名函式頂端。如果瀏覽器不支援 IndexedDB,就會退出函式。接著,您要呼叫 openDB()
方法以開啟名為 'test-db1'
的資料庫。在這個範例中,系統保留了選用的事件物件,以簡化相關流程,不過您最後還是需要指定這個物件,才能使用 IndexedDB 執行任何有意義的工作。
如何使用物件儲存庫
IndexedDB 資料庫包含一或多個物件儲存庫。物件存放區的概念與 SQL 資料庫中的資料表類似。物件儲存庫與 SQL 資料表一樣,包含資料列和資料欄,但在指數資料庫中,索引資料庫物件儲存庫中的欄數和該鍵相關聯的資料欄位數量較少。
建立物件儲存庫
舉例來說,網站會保存使用者個人資料和附註,您可以想像有包含 person
物件的 people
物件儲存庫,以及 notes
物件存放區。結構完善的 IndexedDB 資料庫應針對每種需要保存的資料類型提供一個物件儲存區。
為確保資料庫完整性,只能在 openDB()
呼叫的事件物件中建立和移除物件儲存庫。事件物件會公開 upgrade()
方法,提供建立物件儲存空間的方式。呼叫 upgrade()
方法中的 createObjectStore()
方法來建立物件儲存區:
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();
這個方法會使用物件存放區的名稱,以及可讓您為物件儲存區定義各種屬性的選用設定物件。
以下是 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();
在這個範例中,事件物件會傳遞至 openDB()
方法以建立物件儲存庫,和先前一樣,建立物件存放區的工作會在事件物件的 upgrade()
方法中完成。不過,如果您嘗試建立已存在的物件儲存庫,瀏覽器就會擲回錯誤,因此您將 createObjectStore()
方法納入 if
陳述式,藉此檢查物件存放區是否存在。在 if
區塊中,您可以呼叫 createObjectStore()
以建立名為 'firstOS'
的物件儲存區。
如何定義主鍵
定義物件儲存區時,您可以透過主鍵定義在儲存庫中識別資料的方式。您可以定義金鑰路徑或使用金鑰產生器,來定義主鍵。
金鑰路徑是指一律存在且包含不重複值的屬性。舉例來說,如果是 people
物件存放區,您可以選擇電子郵件地址做為金鑰路徑:
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();
這個範例會建立名為 'people'
的物件儲存庫,並將 email
屬性指派為 keyPath
選項中的主鍵。
您也可以使用金鑰產生器,例如 autoIncrement
。金鑰產生器會為新增至物件儲存庫的每個物件建立專屬值。根據預設,如果您沒有指定鍵,IndexedDB 會建立鍵,並將鍵與資料分開儲存。
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();
這個範例會建立名為 'notes'
的物件儲存庫,並設定系統自動指派主鍵做為自動遞增編號。
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();
這個範例與前述範例類似,但這次自動遞增的值會明確指派給名為 'id'
的屬性。
請根據您的資料選擇要使用哪種方法來定義金鑰。如果資料具有不重複屬性,您可以讓 keyPath
強制要求不重複值。否則,使用自動遞增的值才是合理的。
以下程式碼會建立三個物件儲存庫,示範在物件儲存庫中定義主鍵的各種方式:
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();
如何定義索引
索引是一種物件儲存區,可以依指定屬性從參照物件儲存庫擷取資料。索引位於參照物件儲存空間內,其中包含相同的資料,但會使用指定屬性做為其鍵路徑,而非參照儲存庫的主鍵。建立物件儲存庫時必須建立索引,並且可用來定義資料的獨特限制。
如要建立索引,請對物件儲存例項呼叫 createIndex()
方法:
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();
這個方法會建立並傳回索引物件。物件儲存庫執行個體的 createIndex()
方法,使用新索引的名稱做為第一個引數,第二個引數則參照您要建立索引之資料上的屬性。最後一個引數可讓您定義兩個選項,決定索引的運作方式:unique
和 multiEntry
。如果將 unique
設為 true
,索引就不會允許單一索引鍵重複值。接著,multiEntry
會決定 createIndex()
在已建立索引的屬性是陣列時的行為。如果設為 true
,createIndex()
會在每個陣列元素的索引中加入項目。否則,系統會新增包含該陣列的單一項目。
範例如下:
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();
在這個範例中,'people'
和 'notes'
物件儲存庫都有索引。如要建立索引,您必須先將 createObjectStore()
(這是物件儲存物件) 的結果指派給變數,以便針對變數呼叫 createIndex()
。
如何使用資料
本節說明如何建立、讀取、更新及刪除資料。這些作業全都非同步,使用 promise 讓 IndexedDB API 使用要求。這樣可以簡化 API。您不必監聽要求觸發的事件,可以改為在 openDB()
方法傳回的資料庫物件上呼叫 .then()
,開始與資料庫互動,或 await
建立資料庫。
IndexedDB 中的所有資料作業都會在交易中執行。每項作業都會形成以下形式:
- 取得資料庫物件。
- 在資料庫中開啟交易。
- 開啟交易的物件存放區。
- 在物件存放區執行作業。
交易可視為一種安全的包裝函式,用於作業或一組作業。如果交易中的其中一個動作失敗,所有動作都會復原。交易專屬於您在開啟交易時定義的一或多個物件儲存庫。可以是唯讀或讀取及寫入。這會指出交易中的作業是否讀取資料或變更資料庫。
建立資料
如要建立資料,請對資料庫例項呼叫 add()
方法,然後傳入要新增的資料。add()
方法的第一個引數是要新增資料的物件儲存庫,第二個引數則是包含要新增欄位和相關資料的物件。以下是最簡單的例子,其中會加入單列資料:
import {openDB} from 'idb';
async function addItemToStore () {
const db = await openDB('example-database', 1);
await db.add('storeName', {
field: 'data'
});
}
addItemToStore();
每次 add()
呼叫都會在交易中發生,因此即使 promise 成功解決,也不代表作業可以執行。請記住,如果交易中的其中一個動作失敗,交易中的所有作業都會復原。
為確保新增作業已完成,您需要使用 transaction.done()
方法檢查整筆交易是否已完成。這會在交易完成時解決,並在交易錯誤時拒絕。請注意,這個方法實際上不會完成交易。交易會自行完成。您必須對所有「寫入」作業執行這項檢查,因為這是您瞭解資料庫變更實際上是否已執行的唯一方法。
以下程式碼示範了 add()
方法的用法,但這次使用交易:
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();
開啟資料庫 (並視需要建立物件儲存庫) 後,您必須在交易上呼叫 transaction()
方法來開啟交易。此方法會接受要交易的商店及其模式引數。在本範例中,我們希望寫入商店,因此上述範例中指定 'readwrite'
。
下一步是開始在交易中將商品新增至商店。在上述範例中,我們要處理 'foods'
商店上每個傳回承諾的三項作業:
- 新增美味三明治的記錄。
- 新增一些雞蛋的記錄。
- 表示交易已完成 (
tx.done
)。
上述所有行動都是經承諾的,我們需要等候所有動作完成。將這些承諾傳遞至 Promise.all
,是達成人體工學的絕佳方式。Promise.all
會接受任何 promise 陣列,並在所有承諾都解決後就會結束。
對於要新增的兩個記錄,交易例項的 store
介面具有可呼叫的 add
方法,且資料會傳遞至兩筆記錄。Promise.all
呼叫本身可以 await
,並在交易完成時結束。
讀取資料
如要讀取資料,請在使用 openDB()
方法擷取的資料庫例項上呼叫 get()
方法。get()
會擷取商店名稱,以及要從商店擷取的物件主鍵值。基本範例:
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();
和 add()
一樣,get()
方法會傳回 promise,並視需要 await
例項;或者,如果不需要,請使用 .then()
回呼的所有承諾產品。
以下範例在 'test-db4'
資料庫的 'foods'
物件儲存庫中使用 get()
方法,透過 '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();
如要從資料庫擷取單一資料列,做法很簡單:開啟資料庫,然後指定要取得資料的資料列物件儲存庫和主鍵值。由於 get()
方法會傳回承諾,因此您可以 await
。
更新資料
如要更新資料,請呼叫物件存放區的 put()
方法。put()
方法類似於 add()
方法,也可以用來取代 add()
,以便在物件存放區中建立資料。以下是使用 put()
依據主鍵值更新物件儲存庫中資料列的最簡單範例:
import {openDB} from 'idb';
async function updateItemInStore () {
const db = await openDB('example-database', 1);
// Update a value from in an object store with an in-line 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();
如同其他方法,這個方法會傳回 promise。您也可以使用 put()
做為交易的一部分,就跟使用 add()
方法一樣。以下為使用前述的 'foods'
商店範例,但我們只更新三明治和雞蛋的價格:
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();
項目的更新方式取決於您設定金鑰的方式。如果您設定 keyPath
,物件儲存庫中的每一列都會與所謂的內嵌索引鍵建立關聯。上例會根據此鍵更新資料列,而在這種情況下,您在更新資料列時,必須指定該鍵,物件儲存庫中的適當項目才會實際更新。將 autoIncrement
設為主鍵即可建立非行鍵。
刪除資料
如要刪除資料,請呼叫物件儲存庫上的 delete()
方法:
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();
和 add()
和 put()
一樣,這個項目也可在交易中使用:
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();
資料庫互動的結構與其他作業相同。請注意,在傳遞至 Promise.all
的陣列中加入 tx.done
方法,確認已執行刪除作業,再次檢查整項交易是否已完成。
取得所有資料
目前,您一次只能從商店擷取一個物件。您也可以使用 getAll()
方法或使用遊標,擷取物件儲存庫或索引中的所有資料 (或部分資料)。
使用 getAll()
方法
如要擷取物件存放區的所有資料,最簡單的方法是在物件儲存庫或索引上呼叫 getAll()
方法,如下所示:
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();
這個方法會傳回物件儲存庫中的所有物件,但不會有任何限制。這是從物件儲存庫取得所有值最直接的方法,但也最靈活有彈性。
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();
這裡的 'foods'
物件存放區會呼叫 getAll()
。這樣做會傳回按主鍵排序的儲存庫 'foods'
中的所有物件。
如何使用遊標
如果想擷取所有資料,另一種方法就是使用遊標,而不是一次取得所有資料。遊標會逐一選取物件儲存庫中的各個物件或逐一建立索引,方便您在選取資料的同時處理選取項目。遊標就像其他資料庫作業一樣,會在交易內運作。
您可以針對物件存放區呼叫 openCursor()
方法來建立遊標。這項作業會在交易過程中完成。使用上一個範例的 'foods'
儲存庫,即可將遊標導向物件儲存庫中的所有資料列:
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();
在此情況下,交易會以 'readonly'
模式開啟,並呼叫其 openCursor
方法。在後續的 while
迴圈中,遊標目前位置的資料列可以讀取其 key
和 value
屬性,而且您可以使用最適合應用程式的方式操作這些值。準備就緒後,您可以呼叫 cursor
物件的 continue()
方法前往下一個資料列,且 while
迴圈會在達到資料集結尾時終止。
如何將遊標與範圍和索引搭配使用
你可以透過幾種不同的方式取得所有資料,但如果你只想根據特定資源取得部分資料,該怎麼做?這是索引的來源。索引可讓您依據主鍵以外的屬性,擷取物件儲存空間中的資料。您可以在任何屬性上建立索引 (會成為索引的 keyPath
)、指定該屬性的範圍,並使用 getAll()
方法或遊標取得範圍內的資料。
請使用 IDBKeyRange
物件定義範圍。這個物件提供五個方法,用來定義範圍的限制:
upperBound()
.lowerBound()
.bound()
(兩者皆是)。only()
.includes()
.
一如預期,upperBound()
和 lowerBound()
方法會指定範圍的上限與下限。
IDBKeyRange.lowerBound(indexKey);
或:
IDBKeyRange.upperBound(indexKey);
每個函式都會接受一個引數,也就是您要指定為上限或下限的項目索引的 keyPath
值。
bound()
方法用於指定上限和下限,並以下限值做為第一個引數:
IDBKeyRange.bound(lowerIndexKey, upperIndexKey);
這些函式的範圍預設為包含,但可以指定為排除範圍,方法是將 true
做為第二個引數傳遞 (若為 bound()
,則將值設為最低和上限的第三和第四個引數)。包含範圍限制的資料。排除範圍則無效。
讓我們來看看下面這個例子。在這個示範中,您已在 'foods'
物件儲存庫的 'price'
屬性中建立索引。並新增了具有範圍上限和兩個輸入內容的小表單。假設您要傳入函式中下限和上限,做為代表價格的浮點數:
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);
程式碼會先取得限制的值,並檢查限制是否存在。接下來的程式碼區塊會決定要使用哪種方法根據值來限制範圍。在資料庫互動中,照常開啟交易中的物件儲存庫,然後在物件儲存庫中開啟 'price'
索引。'price'
索引可讓您依價格搜尋商品。
接著,在索引上開啟遊標,並傳遞該範圍。遊標現在會傳回 promise,代表範圍內的第一個物件,如果範圍內沒有資料,則傳回 undefined
。cursor.continue()
方法會傳回代表下一個物件的遊標,以此類推,直到到達範圍結尾為止。
使用資料庫版本管理
呼叫 openDB()
方法時,您可以在第二個參數中指定資料庫版本號碼。在本指南的所有範例中,版本已設為 1
,但如果您需要某種方式進行修改,可以將資料庫升級至新版本。如果指定版本高於現有資料庫的版本,系統會執行事件物件中的 upgrade
回呼,以便您將新的物件儲存及索引新增至資料庫。
upgrade
回呼中的 db
物件具有特殊的 oldVersion
屬性,代表瀏覽器中現有的資料庫目前版本號碼。您可以將這個版本編號傳遞至 switch
陳述式,根據現有的資料庫版本號碼,執行 upgrade
回呼中的程式碼區塊。範例如下:
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');
}
}
});
以下範例會將資料庫的最新版本設為 2
。當此程式碼首次執行時,由於瀏覽器中尚不存在資料庫,oldVersion
為 0
,而 switch
陳述式從 case 0
開始。在本範例中,這會將 'store'
物件儲存庫加入資料庫。
如要在 'store'
物件儲存庫中建立 'description'
索引,請更新版本號碼並新增 case
區塊,如下所示:
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');
}
}
});
假設您在上一個範例中建立的資料庫仍在瀏覽器中,而當執行 oldVersion
時,瀏覽器內依然存在 2
。系統會略過 case 0
和 case 1
,且瀏覽器會在 case 2
中執行程式碼,進而建立 'description'
索引。完成所有操作後,瀏覽器就會建立位於第 3 版的資料庫,其中包含了 'name'
和 'description'
索引的 'store'
物件儲存庫。
其他資訊
使用 IndexedDB 時,可參考下列資源提供更多資訊和背景資訊。