1. IndexedDB简介
IndexedDB
是一种在浏览器中存储结构化数据的方式,类似于关系数据库,但它具有更高的存储容量和灵活性。相比于 localStorage
,IndexedDB
适合存储大量数据,能够处理更复杂的数据结构,并且支持异步操作。它是浏览器端持久化存储的一种常用方式。
IndexedDB的特点:
- 异步:
IndexedDB
操作大多数是异步的,因此不会阻塞 UI 线程。 - 存储数据结构:能够存储 JavaScript 对象,包括数组、对象等结构化数据。
- 大容量:浏览器的存储限制较高,通常比
localStorage
高得多(大多数浏览器在单个域名下允许存储 50MB 以上的数据)。 - 事务支持:支持事务,使得对数据库的读写操作具有原子性。
- 索引:可以为数据创建索引,提高检索效率。
IndexedDB的常见操作:
- 打开数据库:通过
indexedDB.open()
打开(或者创建)数据库。 - 创建对象存储(Object Store):相当于数据库表,用来存储实际的数据。
- 事务(Transaction):所有的操作都在事务中进行,确保数据一致性。
- 数据存取:支持
get
、put
、delete
等操作。 - 索引(Index):可以为数据创建索引,便于按字段查找数据。
2. 封装 IndexedDB
的模块代码
为了简化 IndexedDB
的使用,我们可以创建一些封装函数,以便于更容易地进行数据库操作。以下是一个简单的封装模块,提供了常见的 IndexedDB
操作,如打开数据库、保存数据、获取数据等。
indexedDB.js
模块:
// 打开或创建数据库
export const openDB = (dbName, storeName) => {
return new Promise((resolve, reject) => {
const request = indexedDB.open(dbName, 1); // 打开数据库,版本号设置为1
// 数据库首次创建时或版本升级时会触发
request.onupgradeneeded = (event) => {
const db = event.target.result;
// 如果数据库中没有这个对象存储,则创建它
if (!db.objectStoreNames.contains(storeName)) {
const store = db.createObjectStore(storeName, { keyPath: 'id' }); // 使用 'id' 作为主键
store.createIndex('treeIndex', 'tree'); // 为 'tree' 字段创建索引
}
};
// 数据库打开成功
request.onsuccess = (event) => {
resolve(event.target.result);
};
// 打开失败
request.onerror = (event) => {
reject('数据库打开失败: ' + event.target.errorCode);
};
});
};
// 保存数据到数据库
export const saveToDB = (db, storeName, data) => {
return new Promise((resolve, reject) => {
const transaction = db.transaction(storeName, 'readwrite'); // 以读写模式打开事务
const store = transaction.objectStore(storeName);
const request = store.put(data); // 插入或更新数据
request.onsuccess = () => {
resolve('数据保存成功');
};
request.onerror = (event) => {
reject('数据保存失败: ' + event.target.errorCode);
};
});
};
// 从数据库中获取数据
export const getFromDB = (db, storeName, id) => {
return new Promise((resolve, reject) => {
const transaction = db.transaction(storeName, 'readonly'); // 以只读模式打开事务
const store = transaction.objectStore(storeName);
const request = store.get(id); // 根据ID获取数据
request.onsuccess = (event) => {
resolve(event.target.result); // 返回获取到的结果
};
request.onerror = (event) => {
reject('获取数据失败: ' + event.target.errorCode);
};
});
};
// 删除数据库中的数据
export const deleteFromDB = (db, storeName, id) => {
return new Promise((resolve, reject) => {
const transaction = db.transaction(storeName, 'readwrite');
const store = transaction.objectStore(storeName);
const request = store.delete(id); // 根据ID删除数据
request.onsuccess = () => {
resolve('数据删除成功');
};
request.onerror = (event) => {
reject('数据删除失败: ' + event.target.errorCode);
};
});
};
3. 使用示例
初始化数据库:
const [db, setDb] = useState(null);
useEffect(() => {
const initDB = async () => {
const database = await openDB('CubeDataDB', 'treeStore');
setDb(database);
};
initDB();
}, []);
获取数据:
const getSavedTreeState = async () => {
if (db) {
try {
const savedState = await getFromDB(db, 'treeStore', queryUuid);
return savedState ? savedState.tree : cubeStructTree;
} catch (e) {
console.error('Failed to get tree state from IndexedDB', e);
return cubeStructTree;
}
}
return cubeStructTree;
};
保存数据:
const saveTreeState = async (updatedTree) => {
if (db) {
try {
await saveToDB(db, 'treeStore', { id: queryUuid, tree: updatedTree });
console.log('Tree state saved to IndexedDB');
} catch (e) {
console.error('Failed to save tree state to IndexedDB', e);
}
}
};
删除数据:
const deleteSavedState = async () => {
if (db) {
try {
await deleteFromDB(db, 'treeStore', queryUuid);
console.log('Tree state deleted from IndexedDB');
} catch (e) {
console.error('Failed to delete tree state from IndexedDB', e);
}
}
};
4. 总结
IndexedDB
提供了一个浏览器端的数据库,能够处理大量数据并提供异步接口。- 我们封装了几个常用的
IndexedDB
操作,包括打开数据库、保存数据、获取数据和删除数据。 - 在 React 中,通过
useEffect
打开数据库,并使用封装函数进行数据存取。
通过这种封装方式,你可以方便地在 React 项目中使用 IndexedDB
存储数据,避免了 localStorage
的存储限制,同时支持更复杂的数据结构。