前端 IndexedDB 詳細教程
IndexedDB 是一個瀏覽器內置的 NoSQL 數據庫系統,允許在客戶端存儲大量結構化數據,并支持高性能搜索。相比 localStorage,IndexedDB 更適合存儲大量數據并提供更復雜的查詢功能。
基本概念
- 數據庫:每個源(協議+域名+端口)可以創建多個數據庫
- 對象存儲(Object Store):類似于數據庫中的表
- 索引(Index):用于快速查找數據
- 事務(Transaction):所有操作必須在事務中執行
- 游標(Cursor):用于遍歷對象存儲中的數據
打開/創建數據庫
const request = indexedDB.open('myDatabase', 1);request.onerror = function(event) {console.error("數據庫打開失敗:", event.target.error);
};request.onsuccess = function(event) {const db = event.target.result;console.log("數據庫打開成功");// 在這里執行數據庫操作
};request.onupgradeneeded = function(event) {const db = event.target.result;// 創建對象存儲和索引if (!db.objectStoreNames.contains('customers')) {const store = db.createObjectStore('customers', { keyPath: 'id' });// 創建索引store.createIndex('name', 'name', { unique: false });store.createIndex('email', 'email', { unique: true });}
};
添加數據
function addCustomer(db, customer) {const transaction = db.transaction(['customers'], 'readwrite');const store = transaction.objectStore('customers');const request = store.add(customer);request.onsuccess = function() {console.log('數據添加成功');};request.onerror = function(event) {console.error('數據添加失敗:', event.target.error);};
}// 使用示例
const db = /* 獲取數據庫實例 */;
addCustomer(db, {id: 1,name: '張三',email: 'zhangsan@example.com',age: 30
});
讀取數據
通過主鍵讀取
function getCustomer(db, id) {const transaction = db.transaction(['customers'], 'readonly');const store = transaction.objectStore('customers');const request = store.get(id);request.onsuccess = function() {const customer = request.result;if (customer) {console.log('找到客戶:', customer);} else {console.log('未找到客戶');}};request.onerror = function(event) {console.error('讀取數據失敗:', event.target.error);};
}
通過索引讀取
function getCustomerByName(db, name) {const transaction = db.transaction(['customers'], 'readonly');const store = transaction.objectStore('customers');const index = store.index('name');const request = index.get(name);request.onsuccess = function() {const customer = request.result;console.log('找到客戶:', customer);};request.onerror = function(event) {console.error('通過索引查詢失敗:', event.target.error);};
}
更新數據
function updateCustomer(db, customer) {const transaction = db.transaction(['customers'], 'readwrite');const store = transaction.objectStore('customers');const request = store.put(customer);request.onsuccess = function() {console.log('數據更新成功');};request.onerror = function(event) {console.error('數據更新失敗:', event.target.error);};
}
刪除數據
function deleteCustomer(db, id) {const transaction = db.transaction(['customers'], 'readwrite');const store = transaction.objectStore('customers');const request = store.delete(id);request.onsuccess = function() {console.log('數據刪除成功');};request.onerror = function(event) {console.error('數據刪除失敗:', event.target.error);};
}
使用游標遍歷數據
function getAllCustomers(db) {const transaction = db.transaction(['customers'], 'readonly');const store = transaction.objectStore('customers');const request = store.openCursor();const customers = [];request.onsuccess = function(event) {const cursor = event.target.result;if (cursor) {customers.push(cursor.value);cursor.continue();} else {console.log('所有客戶:', customers);}};request.onerror = function(event) {console.error('遍歷數據失敗:', event.target.error);};
}
高級用法
使用索引范圍查詢
function getCustomersByAgeRange(db, min, max) {const transaction = db.transaction(['customers'], 'readonly');const store = transaction.objectStore('customers');// 假設我們創建了age索引const index = store.index('age');const range = IDBKeyRange.bound(min, max);const request = index.openCursor(range);const customers = [];request.onsuccess = function(event) {const cursor = event.target.result;if (cursor) {customers.push(cursor.value);cursor.continue();} else {console.log('年齡范圍內的客戶:', customers);}};
}
批量操作
function addMultipleCustomers(db, customers) {const transaction = db.transaction(['customers'], 'readwrite');const store = transaction.objectStore('customers');customers.forEach(customer => {store.add(customer);});transaction.oncomplete = function() {console.log('批量添加完成');};transaction.onerror = function(event) {console.error('批量操作失敗:', event.target.error);};
}
數據庫版本管理
// 升級數據庫版本
function upgradeDB() {const request = indexedDB.open('myDatabase', 2); // 版本號增加request.onupgradeneeded = function(event) {const db = event.target.result;const oldVersion = event.oldVersion;const newVersion = event.newVersion;// 從版本1升級到版本2if (oldVersion < 1) {// 初始創建邏輯}if (oldVersion < 2) {// 版本2的變更if (!db.objectStoreNames.contains('orders')) {const store = db.createObjectStore('orders', { keyPath: 'orderId' });store.createIndex('customerId', 'customerId', { unique: false });}}};
}
最佳實踐
- 錯誤處理:始終處理onerror事件
- 事務管理:合理使用事務,避免長時間持有事務
- 性能優化:對于大量數據操作,使用游標分批處理
- 內存管理:處理完數據后關閉游標和數據庫連接
- 兼容性:檢查瀏覽器支持情況
if (!window.indexedDB) {console.error("您的瀏覽器不支持IndexedDB");
}
封裝示例
class IndexedDBWrapper {constructor(dbName, version) {this.dbName = dbName;this.version = version;this.db = null;}open() {return new Promise((resolve, reject) => {const request = indexedDB.open(this.dbName, this.version);request.onerror = (event) => reject(event.target.error);request.onsuccess = (event) => {this.db = event.target.result;resolve(this.db);};request.onupgradeneeded = (event) => {const db = event.target.result;if (!db.objectStoreNames.contains('data')) {const store = db.createObjectStore('data', { keyPath: 'id' });store.createIndex('timestamp', 'timestamp', { unique: false });}};});}add(storeName, data) {return new Promise((resolve, reject) => {const transaction = this.db.transaction([storeName], 'readwrite');const store = transaction.objectStore(storeName);const request = store.add(data);request.onsuccess = () => resolve(request.result);request.onerror = (event) => reject(event.target.error);});}// 其他方法類似封裝...
}// 使用示例
(async function() {const dbWrapper = new IndexedDBWrapper('myAppDB', 1);try {await dbWrapper.open();await dbWrapper.add('data', { id: 1, value: 'test', timestamp: Date.now() });console.log('操作成功');} catch (error) {console.error('操作失敗:', error);}
})();
IndexedDB 提供了強大的客戶端存儲能力,適合需要離線功能或處理大量結構化數據的 Web 應用。通過合理使用,可以顯著提升應用性能和用戶體驗。