IndexedDB 是浏览器提供的客户端存储 API,支持存储大量结构化数据,包括文件/二进制大型对象(blobs)。
- 数据库 (Database): 顶层容器,包含多个对象存储
- 对象存储 (Object Store): 类似于关系数据库中的表
- 事务 (Transaction): 操作的原子单位
- 索引 (Index): 用于快速查找数据
- 键路径 (Key Path): 对象中作为主键的属性
// 打开数据库(如果不存在则创建)
const request = indexedDB.open('数据库名称', 版本号);
request.onsuccess = function(event) {
const db = event.target.result;
console.log('数据库连接成功');
};
request.onerror = function(event) {
console.error('数据库连接失败:', event.target.error);
};
request.onupgradeneeded = function(event) {
const db = event.target.result;
// 数据库升级时执行
};const deleteRequest = indexedDB.deleteDatabase('数据库名称');
deleteRequest.onsuccess = function() {
console.log('数据库删除成功');
};
deleteRequest.onerror = function(event) {
console.error('数据库删除失败:', event.target.error);
};if (!window.indexedDB) {
console.log('当前浏览器不支持 IndexedDB');
} else {
console.log('浏览器支持 IndexedDB');
}// 在 onupgradeneeded 事件中创建
request.onupgradeneeded = function(event) {
const db = event.target.result;
// 创建对象存储
const objectStore = db.createObjectStore('存储名称', {
keyPath: 'id', // 主键字段
autoIncrement: true // 自动递增
});
// 或者使用外部键
const objectStore2 = db.createObjectStore('存储名称2');
};// 在 onupgradeneeded 事件中删除
request.onupgradeneeded = function(event) {
const db = event.target.result;
if (db.objectStoreNames.contains('存储名称')) {
db.deleteObjectStore('存储名称');
}
};// 在创建对象存储时或 onupgradeneeded 中创建索引
const objectStore = db.createObjectStore('users', {
keyPath: 'id',
autoIncrement: true
});
// 创建索引
objectStore.createIndex('name', 'name', { unique: false });
objectStore.createIndex('email', 'email', { unique: true });
objectStore.createIndex('age', 'age', { unique: false });// 在 onupgradeneeded 事件中删除索引
request.onupgradeneeded = function(event) {
const db = event.target.result;
const transaction = event.target.transaction;
const objectStore = transaction.objectStore('users');
if (objectStore.indexNames.contains('索引名称')) {
objectStore.deleteIndex('索引名称');
}
};// 只读事务
const transaction = db.transaction(['存储名称'], 'readonly');
// 读写事务
const transaction = db.transaction(['存储名称'], 'readwrite');
// 多个存储的事务
const transaction = db.transaction(['存储1', '存储2'], 'readwrite');transaction.oncomplete = function() {
console.log('事务完成');
};
transaction.onerror = function(event) {
console.error('事务失败:', event.target.error);
};
transaction.onabort = function() {
console.log('事务被中止');
};const transaction = db.transaction(['users'], 'readwrite');
const objectStore = transaction.objectStore('users');
const user = {
name: '张三',
email: 'zhangsan@example.com',
age: 25
};
const request = objectStore.add(user);
request.onsuccess = function(event) {
console.log('数据添加成功,ID:', event.target.result);
};
request.onerror = function(event) {
console.error('数据添加失败:', event.target.error);
};const transaction = db.transaction(['users'], 'readonly');
const objectStore = transaction.objectStore('users');
const request = objectStore.get(1); // 获取 ID 为 1 的记录
request.onsuccess = function(event) {
const user = event.target.result;
if (user) {
console.log('找到用户:', user);
} else {
console.log('用户不存在');
}
};const transaction = db.transaction(['users'], 'readonly');
const objectStore = transaction.objectStore('users');
const request = objectStore.getAll();
request.onsuccess = function(event) {
const users = event.target.result;
console.log('所有用户:', users);
};const transaction = db.transaction(['users'], 'readonly');
const objectStore = transaction.objectStore('users');
const index = objectStore.index('email');
const request = index.get('zhangsan@example.com');
request.onsuccess = function(event) {
const user = event.target.result;
console.log('通过邮箱找到用户:', user);
};const transaction = db.transaction(['users'], 'readonly');
const objectStore = transaction.objectStore('users');
const index = objectStore.index('age');
// 年龄在 20-30 之间
const range = IDBKeyRange.bound(20, 30);
const request = index.getAll(range);
request.onsuccess = function(event) {
const users = event.target.result;
console.log('年龄在 20-30 之间的用户:', users);
};const transaction = db.transaction(['users'], 'readwrite');
const objectStore = transaction.objectStore('users');
const updatedUser = {
id: 1,
name: '李四',
email: 'lisi@example.com',
age: 28
};
const request = objectStore.put(updatedUser);
request.onsuccess = function() {
console.log('用户更新成功');
};const transaction = db.transaction(['users'], 'readwrite');
const objectStore = transaction.objectStore('users');
const request = objectStore.delete(1); // 删除 ID 为 1 的记录
request.onsuccess = function() {
console.log('用户删除成功');
};const transaction = db.transaction(['users'], 'readwrite');
const objectStore = transaction.objectStore('users');
const request = objectStore.clear();
request.onsuccess = function() {
console.log('所有数据已清空');
};const transaction = db.transaction(['users'], 'readonly');
const objectStore = transaction.objectStore('users');
const request = objectStore.openCursor();
request.onsuccess = function(event) {
const cursor = event.target.result;
if (cursor) {
console.log('当前记录:', cursor.value);
cursor.continue(); // 移动到下一条记录
} else {
console.log('遍历完成');
}
};const transaction = db.transaction(['users'], 'readonly');
const objectStore = transaction.objectStore('users');
const index = objectStore.index('name');
const request = index.openCursor();
request.onsuccess = function(event) {
const cursor = event.target.result;
if (cursor) {
console.log('用户:', cursor.value);
cursor.continue();
}
};const transaction = db.transaction(['users'], 'readonly');
const objectStore = transaction.objectStore('users');
const index = objectStore.index('age');
const range = IDBKeyRange.lowerBound(18); // 年龄 >= 18
const request = index.openCursor(range);
request.onsuccess = function(event) {
const cursor = event.target.result;
if (cursor) {
console.log('成年用户:', cursor.value);
cursor.continue();
}
};// 等于特定值
const exactRange = IDBKeyRange.only(25);
// 大于等于
const lowerRange = IDBKeyRange.lowerBound(18);
// 大于(不包含边界)
const lowerOpenRange = IDBKeyRange.lowerBound(18, true);
// 小于等于
const upperRange = IDBKeyRange.upperBound(65);
// 小于(不包含边界)
const upperOpenRange = IDBKeyRange.upperBound(65, true);
// 范围(包含边界)
const boundRange = IDBKeyRange.bound(18, 65);
// 范围(不包含边界)
const boundOpenRange = IDBKeyRange.bound(18, 65, true, true);request.onerror = function(event) {
const error = event.target.error;
switch (error.name) {
case 'QuotaExceededError':
console.error('存储空间不足');
break;
case 'InvalidStateError':
console.error('数据库状态无效');
break;
case 'ConstraintError':
console.error('约束错误(如唯一索引冲突)');
break;
case 'DataError':
console.error('数据错误');
break;
default:
console.error('未知错误:', error);
}
};function openDB(name, version) {
return new Promise((resolve, reject) => {
const request = indexedDB.open(name, version);
request.onsuccess = () => resolve(request.result);
request.onerror = () => reject(request.error);
request.onupgradeneeded = (event) => {
// 数据库升级逻辑
};
});
}
function addData(db, storeName, data) {
return new Promise((resolve, reject) => {
const transaction = db.transaction([storeName], 'readwrite');
const store = transaction.objectStore(storeName);
const request = store.add(data);
request.onsuccess = () => resolve(request.result);
request.onerror = () => reject(request.error);
});
}const DB_VERSION = 1;
function upgradeDB(event) {
const db = event.target.result;
const oldVersion = event.oldVersion;
const newVersion = event.newVersion;
if (oldVersion < 1) {
// 创建初始结构
const userStore = db.createObjectStore('users', {
keyPath: 'id',
autoIncrement: true
});
userStore.createIndex('email', 'email', { unique: true });
}
if (oldVersion < 2) {
// 添加新的索引或存储
}
}async function retryOperation(operation, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
try {
return await operation();
} catch (error) {
if (i === maxRetries - 1) throw error;
console.warn(`操作失败,正在重试 (${i + 1}/${maxRetries}):`, error);
await new Promise(resolve => setTimeout(resolve, 1000 * (i + 1)));
}
}
}// 导出数据
async function exportData(db, storeName) {
const transaction = db.transaction([storeName], 'readonly');
const store = transaction.objectStore(storeName);
const request = store.getAll();
return new Promise((resolve, reject) => {
request.onsuccess = () => {
const data = request.result;
const json = JSON.stringify(data, null, 2);
resolve(json);
};
request.onerror = () => reject(request.error);
});
}
// 导入数据
async function importData(db, storeName, jsonData) {
const data = JSON.parse(jsonData);
const transaction = db.transaction([storeName], 'readwrite');
const store = transaction.objectStore(storeName);
// 清空现有数据
await store.clear();
// 导入新数据
for (const item of data) {
await store.add(item);
}
}- 使用事务批量操作数据
- 合理设计索引,避免过多索引
- 使用游标遍历大量数据
- 避免在主线程进行大量数据操作
- 适当使用 Web Workers 处理复杂操作
- IE 10+ 支持 IndexedDB
- Safari 在私有模式下有限制
- 某些移动浏览器可能有存储限制
- 建议使用 polyfill 提升兼容性