Search K
Appearance
Appearance
📊 SEO元描述:2024年最新JavaScript IndexedDB教程,详解客户端数据库操作、事务处理、异步API使用。包含完整代码示例和最佳实践,适合前端开发者快速掌握IndexedDB数据库技术。
核心关键词:JavaScript IndexedDB 2024、IndexedDB教程、客户端数据库、IndexedDB事务、浏览器数据库、前端数据库技术
长尾关键词:IndexedDB怎么使用、IndexedDB和localStorage区别、IndexedDB事务处理、IndexedDB异步操作、前端离线数据库
通过本节JavaScript IndexedDB详解,你将系统性掌握:
IndexedDB是什么?这是现代Web应用开发者必须了解的问题。IndexedDB是浏览器内置的NoSQL数据库,提供了事务性的数据存储能力,也是大容量客户端数据管理的最佳解决方案。
💡 技术优势:IndexedDB相比其他存储方案具有更大容量、更强功能、更好性能的优势,是复杂Web应用的理想选择
// 📊 存储方案对比表
const storageComparison = {
Cookie: {
capacity: '4KB',
persistence: '可设置过期时间',
scope: '同域名共享',
serverTransfer: '自动发送',
api: '字符串操作',
useCase: '用户认证、偏好设置'
},
localStorage: {
capacity: '5-10MB',
persistence: '持久存储',
scope: '同源共享',
serverTransfer: '不发送',
api: '键值对操作',
useCase: '用户偏好、缓存数据'
},
sessionStorage: {
capacity: '5-10MB',
persistence: '会话期间',
scope: '单标签页',
serverTransfer: '不发送',
api: '键值对操作',
useCase: '临时数据、表单状态'
},
IndexedDB: {
capacity: '几百MB-几GB',
persistence: '持久存储',
scope: '同源共享',
serverTransfer: '不发送',
api: '数据库操作',
useCase: '离线应用、大量结构化数据'
}
};IndexedDB适用场景:
IndexedDB的所有操作都从打开数据库开始:
// 🗄️ 数据库连接基础示例
function openDatabase(dbName, version = 1) {
return new Promise((resolve, reject) => {
const request = indexedDB.open(dbName, version);
// 数据库打开成功
request.onsuccess = (event) => {
const db = event.target.result;
console.log('数据库打开成功:', db.name);
resolve(db);
};
// 数据库打开失败
request.onerror = (event) => {
console.error('数据库打开失败:', event.target.error);
reject(event.target.error);
};
// 数据库需要升级(首次创建或版本变更)
request.onupgradeneeded = (event) => {
const db = event.target.result;
console.log('数据库升级中...');
// 创建对象存储空间(表)
if (!db.objectStoreNames.contains('users')) {
const userStore = db.createObjectStore('users', {
keyPath: 'id',
autoIncrement: true
});
// 创建索引
userStore.createIndex('email', 'email', { unique: true });
userStore.createIndex('name', 'name', { unique: false });
userStore.createIndex('age', 'age', { unique: false });
}
};
});
}
// 使用示例
async function initDatabase() {
try {
const db = await openDatabase('MyAppDB', 1);
console.log('数据库初始化完成');
return db;
} catch (error) {
console.error('数据库初始化失败:', error);
}
}// 🚀 IndexedDB管理工具类
class IndexedDBManager {
constructor(dbName, version = 1) {
this.dbName = dbName;
this.version = version;
this.db = null;
}
// 初始化数据库
async init(storeConfigs) {
return new Promise((resolve, reject) => {
const request = indexedDB.open(this.dbName, this.version);
request.onsuccess = (event) => {
this.db = event.target.result;
resolve(this.db);
};
request.onerror = (event) => {
reject(event.target.error);
};
request.onupgradeneeded = (event) => {
const db = event.target.result;
// 根据配置创建存储空间
storeConfigs.forEach(config => {
if (!db.objectStoreNames.contains(config.name)) {
const store = db.createObjectStore(config.name, config.options);
// 创建索引
if (config.indexes) {
config.indexes.forEach(index => {
store.createIndex(index.name, index.keyPath, index.options);
});
}
}
});
};
});
}
// 获取事务
getTransaction(storeNames, mode = 'readonly') {
return this.db.transaction(storeNames, mode);
}
// 获取对象存储空间
getStore(storeName, mode = 'readonly') {
const transaction = this.getTransaction([storeName], mode);
return transaction.objectStore(storeName);
}
// 关闭数据库
close() {
if (this.db) {
this.db.close();
this.db = null;
}
}
}
// 使用示例
const dbManager = new IndexedDBManager('MyAppDB', 1);
// 数据库配置
const storeConfigs = [
{
name: 'users',
options: { keyPath: 'id', autoIncrement: true },
indexes: [
{ name: 'email', keyPath: 'email', options: { unique: true } },
{ name: 'name', keyPath: 'name', options: { unique: false } }
]
},
{
name: 'posts',
options: { keyPath: 'id', autoIncrement: true },
indexes: [
{ name: 'userId', keyPath: 'userId', options: { unique: false } },
{ name: 'createdAt', keyPath: 'createdAt', options: { unique: false } }
]
}
];
// 初始化数据库
await dbManager.init(storeConfigs);IndexedDB的所有数据操作都必须在事务中进行:
// 📝 事务处理示例
class TransactionManager {
constructor(db) {
this.db = db;
}
// 执行事务操作
async executeTransaction(storeNames, mode, operations) {
return new Promise((resolve, reject) => {
const transaction = this.db.transaction(storeNames, mode);
const results = [];
// 事务完成
transaction.oncomplete = () => {
console.log('事务执行完成');
resolve(results);
};
// 事务失败
transaction.onerror = (event) => {
console.error('事务执行失败:', event.target.error);
reject(event.target.error);
};
// 事务中止
transaction.onabort = (event) => {
console.warn('事务被中止:', event.target.error);
reject(new Error('Transaction aborted'));
};
// 执行操作
try {
operations(transaction, results);
} catch (error) {
transaction.abort();
reject(error);
}
});
}
}// 🔨 完整的CRUD操作类
class IndexedDBCRUD {
constructor(dbManager) {
this.dbManager = dbManager;
}
// 创建数据
async create(storeName, data) {
return new Promise((resolve, reject) => {
const store = this.dbManager.getStore(storeName, 'readwrite');
const request = store.add(data);
request.onsuccess = (event) => {
resolve(event.target.result); // 返回生成的key
};
request.onerror = (event) => {
reject(event.target.error);
};
});
}
// 读取数据
async read(storeName, key) {
return new Promise((resolve, reject) => {
const store = this.dbManager.getStore(storeName, 'readonly');
const request = store.get(key);
request.onsuccess = (event) => {
resolve(event.target.result);
};
request.onerror = (event) => {
reject(event.target.error);
};
});
}
// 更新数据
async update(storeName, data) {
return new Promise((resolve, reject) => {
const store = this.dbManager.getStore(storeName, 'readwrite');
const request = store.put(data);
request.onsuccess = (event) => {
resolve(event.target.result);
};
request.onerror = (event) => {
reject(event.target.error);
};
});
}
// 删除数据
async delete(storeName, key) {
return new Promise((resolve, reject) => {
const store = this.dbManager.getStore(storeName, 'readwrite');
const request = store.delete(key);
request.onsuccess = () => {
resolve(true);
};
request.onerror = (event) => {
reject(event.target.error);
};
});
}
// 获取所有数据
async getAll(storeName) {
return new Promise((resolve, reject) => {
const store = this.dbManager.getStore(storeName, 'readonly');
const request = store.getAll();
request.onsuccess = (event) => {
resolve(event.target.result);
};
request.onerror = (event) => {
reject(event.target.error);
};
});
}
// 按索引查询
async getByIndex(storeName, indexName, value) {
return new Promise((resolve, reject) => {
const store = this.dbManager.getStore(storeName, 'readonly');
const index = store.index(indexName);
const request = index.get(value);
request.onsuccess = (event) => {
resolve(event.target.result);
};
request.onerror = (event) => {
reject(event.target.error);
};
});
}
// 范围查询
async getRange(storeName, indexName, lowerBound, upperBound) {
return new Promise((resolve, reject) => {
const store = this.dbManager.getStore(storeName, 'readonly');
const index = store.index(indexName);
const range = IDBKeyRange.bound(lowerBound, upperBound);
const request = index.getAll(range);
request.onsuccess = (event) => {
resolve(event.target.result);
};
request.onerror = (event) => {
reject(event.target.error);
};
});
}
}
// 使用示例
const crud = new IndexedDBCRUD(dbManager);
// 创建用户
const userId = await crud.create('users', {
name: 'John Doe',
email: 'john@example.com',
age: 30,
createdAt: new Date()
});
// 读取用户
const user = await crud.read('users', userId);
// 更新用户
await crud.update('users', {
id: userId,
name: 'John Smith',
email: 'john.smith@example.com',
age: 31,
updatedAt: new Date()
});
// 按邮箱查询用户
const userByEmail = await crud.getByIndex('users', 'email', 'john.smith@example.com');
// 查询年龄范围内的用户
const usersInAgeRange = await crud.getRange('users', 'age', 25, 35);// 🔄 游标操作类
class CursorOperations {
constructor(dbManager) {
this.dbManager = dbManager;
}
// 使用游标遍历数据
async forEach(storeName, callback, indexName = null) {
return new Promise((resolve, reject) => {
const store = this.dbManager.getStore(storeName, 'readonly');
const source = indexName ? store.index(indexName) : store;
const request = source.openCursor();
request.onsuccess = (event) => {
const cursor = event.target.result;
if (cursor) {
callback(cursor.value, cursor.key);
cursor.continue();
} else {
resolve();
}
};
request.onerror = (event) => {
reject(event.target.error);
};
});
}
// 批量更新
async batchUpdate(storeName, updateFunction) {
return new Promise((resolve, reject) => {
const store = this.dbManager.getStore(storeName, 'readwrite');
const request = store.openCursor();
let updateCount = 0;
request.onsuccess = (event) => {
const cursor = event.target.result;
if (cursor) {
const updatedValue = updateFunction(cursor.value);
if (updatedValue) {
cursor.update(updatedValue);
updateCount++;
}
cursor.continue();
} else {
resolve(updateCount);
}
};
request.onerror = (event) => {
reject(event.target.error);
};
});
}
// 条件删除
async deleteWhere(storeName, condition) {
return new Promise((resolve, reject) => {
const store = this.dbManager.getStore(storeName, 'readwrite');
const request = store.openCursor();
let deleteCount = 0;
request.onsuccess = (event) => {
const cursor = event.target.result;
if (cursor) {
if (condition(cursor.value)) {
cursor.delete();
deleteCount++;
}
cursor.continue();
} else {
resolve(deleteCount);
}
};
request.onerror = (event) => {
reject(event.target.error);
};
});
}
}
// 使用示例
const cursorOps = new CursorOperations(dbManager);
// 遍历所有用户
await cursorOps.forEach('users', (user, key) => {
console.log(`用户 ${key}:`, user.name);
});
// 批量更新:给所有用户添加更新时间
const updateCount = await cursorOps.batchUpdate('users', (user) => {
return { ...user, lastUpdated: new Date() };
});
// 删除所有未激活的用户
const deleteCount = await cursorOps.deleteWhere('users', (user) => {
return !user.isActive;
});通过本节JavaScript IndexedDB详解的学习,你已经掌握:
A: IndexedDB是NoSQL数据库,使用对象存储而非表结构,支持JavaScript对象直接存储,没有SQL查询语言,但提供了索引和游标查询功能。
A: IndexedDB数据持久存储,但在存储空间不足时可能被浏览器清理。可以使用Persistent Storage API申请持久存储权限。
A: 在onupgradeneeded事件中处理版本升级,可以创建新的对象存储空间、添加索引、迁移数据等操作。
A: IndexedDB本身是单线程的,但可以在Web Worker中使用,实现后台数据处理而不阻塞主线程。
A: 合理使用索引、避免大事务、使用游标分批处理大量数据、缓存频繁查询的结果等策略可以提升性能。
// 问题:数据库版本冲突或损坏
// 解决:实现版本检测和错误恢复
async function safeOpenDatabase(dbName, version) {
try {
return await openDatabase(dbName, version);
} catch (error) {
if (error.name === 'VersionError') {
// 版本冲突,删除数据库重新创建
await deleteDatabase(dbName);
return await openDatabase(dbName, version);
}
throw error;
}
}// 问题:长时间运行的事务可能超时
// 解决:分批处理大量数据
async function batchProcess(data, batchSize = 100) {
for (let i = 0; i < data.length; i += batchSize) {
const batch = data.slice(i, i + batchSize);
await processBatch(batch);
// 给浏览器时间处理其他任务
await new Promise(resolve => setTimeout(resolve, 0));
}
}"IndexedDB是现代Web应用数据管理的强大工具,通过本节学习,你已经掌握了客户端数据库的核心技术。继续探索存储方案选择和性能优化,构建更强大的Web应用!"