IndexedDB的基本概念与使用
IndexedDB是HTML5提供的一种浏览器端数据库,适合存储大量结构化数据。它基于键值对存储,支持事务处理,能够在离线状态下工作,适合复杂Web应用的数据管理需求。
IndexedDB的核心概念
IndexedDB的核心概念包括数据库、对象仓库、索引和事务。数据库是最高层次的容器,每个数据库包含多个对象仓库。对象仓库类似于关系型数据库中的表,用于存储实际数据。索引用于快速查询数据,事务则保证数据操作的原子性。
数据库采用版本控制机制,每次结构变更都需要升级版本号。例如创建新对象仓库或修改现有结构时,必须通过版本升级来实现:
const request = indexedDB.open('myDatabase', 2);
request.onupgradeneeded = (event) => {
const db = event.target.result;
if (!db.objectStoreNames.contains('users')) {
db.createObjectStore('users', { keyPath: 'id' });
}
};
数据库的打开与关闭
打开数据库使用indexedDB.open()
方法,该方法返回一个IDBRequest对象。第一个参数是数据库名称,第二个可选参数是版本号。如果数据库不存在,会自动创建。
const openRequest = indexedDB.open('myDatabase', 1);
openRequest.onerror = (event) => {
console.error('数据库打开失败:', event.target.error);
};
openRequest.onsuccess = (event) => {
const db = event.target.result;
console.log('数据库打开成功');
// 使用完毕后关闭连接
db.close();
};
注意关闭数据库连接很重要,特别是在SPA应用中,避免内存泄漏。
对象仓库操作
对象仓库是IndexedDB存储数据的基本单位。创建对象仓库只能在onupgradeneeded
事件中完成。创建时可以指定主键和自动增量选项:
request.onupgradeneeded = (event) => {
const db = event.target.result;
// 创建主键为id的对象仓库
const store = db.createObjectStore('books', {
keyPath: 'id',
autoIncrement: true
});
// 创建索引
store.createIndex('by_title', 'title', { unique: false });
store.createIndex('by_author', 'author', { unique: false });
};
数据增删改查
IndexedDB的所有数据操作都必须在事务中进行。事务分为只读和读写两种模式。
添加数据示例:
const transaction = db.transaction(['books'], 'readwrite');
const store = transaction.objectStore('books');
const book = {
title: 'JavaScript高级程序设计',
author: 'Nicholas C. Zakas',
year: 2020
};
const request = store.add(book);
request.onsuccess = () => {
console.log('数据添加成功');
};
查询数据可以通过主键或索引:
const transaction = db.transaction(['books'], 'readonly');
const store = transaction.objectStore('books');
const index = store.index('by_title');
const request = index.get('JavaScript高级程序设计');
request.onsuccess = (event) => {
const book = event.target.result;
console.log(book);
};
事务处理
IndexedDB的事务是自动提交的,不需要手动调用提交方法。但需要注意事务的生命周期:
const transaction = db.transaction(['books'], 'readwrite');
const store = transaction.objectStore('books');
// 事务自动超时时间为60秒
transaction.oncomplete = () => {
console.log('事务完成');
};
transaction.onerror = (event) => {
console.error('事务出错:', event.target.error);
};
// 所有操作必须在事务活跃期间完成
setTimeout(() => {
// 这会导致错误,因为事务可能已经结束
store.add({ title: '超时添加的书' });
}, 1000);
游标遍历
对于大量数据的遍历,需要使用游标:
const transaction = db.transaction(['books'], 'readonly');
const store = transaction.objectStore('books');
const request = store.openCursor();
request.onsuccess = (event) => {
const cursor = event.target.result;
if (cursor) {
console.log(cursor.key, cursor.value);
cursor.continue();
} else {
console.log('遍历结束');
}
};
可以结合索引和游标实现范围查询:
const range = IDBKeyRange.bound('A', 'M');
const index = store.index('by_title');
const request = index.openCursor(range);
request.onsuccess = (event) => {
const cursor = event.target.result;
if (cursor) {
console.log(cursor.value.title);
cursor.continue();
}
};
性能优化
IndexedDB使用时需要注意几个性能要点:
- 批量操作使用事务合并
- 合理使用索引加速查询
- 避免在事务中执行耗时操作
- 大型数据分块处理
批量写入示例:
function addMultipleBooks(db, books) {
return new Promise((resolve, reject) => {
const transaction = db.transaction(['books'], 'readwrite');
const store = transaction.objectStore('books');
transaction.oncomplete = () => resolve();
transaction.onerror = (event) => reject(event.target.error);
books.forEach(book => {
store.add(book);
});
});
}
错误处理
IndexedDB操作可能产生的错误需要妥善处理:
const request = indexedDB.open('myDatabase');
request.onerror = (event) => {
const error = event.target.error;
if (error.name === 'VersionError') {
console.error('版本号低于当前版本');
} else if (error.name === 'QuotaExceededError') {
console.error('存储空间不足');
} else {
console.error('数据库错误:', error);
}
};
实际应用场景
IndexedDB适合以下场景:
- 离线Web应用数据存储
- 客户端大数据缓存
- 需要复杂查询的本地数据
- 需要事务支持的数据操作
例如实现一个离线笔记应用:
class NotesDB {
constructor() {
this.dbPromise = new Promise((resolve, reject) => {
const request = indexedDB.open('NotesDB', 1);
request.onupgradeneeded = (event) => {
const db = event.target.result;
if (!db.objectStoreNames.contains('notes')) {
const store = db.createObjectStore('notes', {
keyPath: 'id',
autoIncrement: true
});
store.createIndex('by_date', 'createdAt', { unique: false });
}
};
request.onsuccess = () => resolve(request.result);
request.onerror = () => reject(request.error);
});
}
async addNote(note) {
const db = await this.dbPromise;
return new Promise((resolve, reject) => {
const transaction = db.transaction(['notes'], 'readwrite');
const store = transaction.objectStore('notes');
const request = store.add({
...note,
createdAt: new Date()
});
request.onsuccess = () => resolve(request.result);
request.onerror = () => reject(request.error);
});
}
async getNotes() {
const db = await this.dbPromise;
return new Promise((resolve, reject) => {
const transaction = db.transaction(['notes'], 'readonly');
const store = transaction.objectStore('notes');
const index = store.index('by_date');
const request = index.getAll();
request.onsuccess = () => resolve(request.result);
request.onerror = () => reject(request.error);
});
}
}
浏览器兼容性
现代浏览器基本都支持IndexedDB,但需要注意:
- IE10+支持但前缀不同(
msIndexedDB
) - 移动端浏览器支持良好
- 隐私模式下可能有限制
检测浏览器支持情况:
if (!window.indexedDB) {
console.warn('当前浏览器不支持IndexedDB');
// 可以考虑降级方案如localStorage
}
调试工具
浏览器开发者工具提供IndexedDB调试功能:
- Chrome DevTools的Application面板
- Firefox的Storage Inspector
- Edge的IndexedDB查看器
这些工具可以查看数据库结构、存储内容和执行简单查询。
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn