IndexedDB - 游标
IndexedDB - 使用游标迭代
Section titled “IndexedDB - 使用游标迭代”尽管 get() 和 getAll() 等方法非常适合检索特定记录或完整数据集,但如果数据集非常大,它们可能会效率低下,因为 getAll() 会将所有记录加载到内存中。游标 (Cursor) 提供了一种强大且内存高效的方式,可以逐个迭代对象仓库 (object store) 或索引 (index) 中的记录。
游标就像一个指针,它在您的记录中移动。您打开一个游标,处理游标当前位置的记录,然后指示它前进到下一条。
现代替代方案:getAll() 和 getAllKeys()
Section titled “现代替代方案:getAll() 和 getAllKeys()”在深入了解游标之前,请始终考虑是否有更简单的方法适合您的需求。对于中等大小的数据集,getAll() 通常就足够了,并且可以使代码更简单。
// Get all objects from the storeconst allRecords = await store.getAll();
// Get all keys from the storeconst allKeys = await store.getAllKeys();在以下情况下使用游标:
- 迭代大量记录而不会占用过多内存。
- 根据特定逻辑逐个处理、更新或删除记录。
- 实现仅靠
getAll()和键范围无法完成的复杂查询。
使用 openCursor()
Section titled “使用 openCursor()”您可以在对象仓库 (object store) 或索引 (index) 上使用 openCursor() 方法创建游标。该方法是异步的,并返回一个 IDBRequest 对象。
objectStore.openCursor(range, direction);- range(可选): 一个
IDBKeyRange,用于将迭代限制在特定键。如果为 null,则迭代整个存储。 - direction(可选): 一个字符串,指定迭代方向。可以是
'next'(默认)、'prev'、'nextunique'或'prevunique'。
示例:迭代记录
Section titled “示例:迭代记录”这是一个完整的 HTML 示例,展示了如何使用游标从对象仓库中读取所有记录。
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>IndexedDB Cursors</title></head><body> <h1>Reading data with a cursor. Check the console.</h1> <ul id="user-list"></ul>
<script> const DB_NAME = "UserDB"; const STORE_NAME = "users"; const userList = document.getElementById('user-list');
// --- 数据库设置(为简洁起见已省略,假定已设置)--- async function setupAndPopulate() { return new Promise((resolve, reject) => { const request = indexedDB.open(DB_NAME, 1); request.onupgradeneeded = e => { const db = e.target.result; const store = db.createObjectStore(STORE_NAME, { keyPath: 'id' }); store.createIndex('by_name', 'name', { unique: false }); // Populate with sample data store.put({ id: 1, name: 'Alice', role: 'Admin' }); store.put({ id: 2, name: 'Bob', role: 'Editor' }); store.put({ id: 3, name: 'Charlie', role: 'Viewer' }); store.put({ id: 4, name: 'Diana', role: 'Editor' }); }; request.onsuccess = e => resolve(e.target.result); request.onerror = e => reject(e.target.error); }); }
async function iterateWithCursor(db) { return new Promise((resolve, reject) => { const transaction = db.transaction(STORE_NAME, 'readonly'); const store = transaction.objectStore(STORE_NAME); const request = store.openCursor();
request.onsuccess = event => { const cursor = event.target.result; if (cursor) { // We have a record const user = cursor.value; console.log(`Key: ${cursor.key}, Value:`, user);
// Display it in the UI const li = document.createElement('li'); li.textContent = `ID: ${user.id}, Name: ${user.name}, Role: ${user.role}`; userList.appendChild(li);
// Move to the next record cursor.continue(); } else { // No more records console.log('Cursor finished.'); resolve(); } };
request.onerror = event => { reject(event.target.error); }; }); }
async function main() { // Clear previous DB for a clean run indexedDB.deleteDatabase(DB_NAME).onsuccess = async () => { const db = await setupAndPopulate(); await iterateWithCursor(db); db.close(); } }
main(); </script></body></html>高级游标操作
Section titled “高级游标操作”- 更新记录: 在
'readwrite'事务中,您可以调用cursor.update(newValue)来修改游标当前位置的记录。 - 删除记录: 类似地,在
'readwrite'事务中,cursor.delete()将移除当前记录。
例如,要将所有“Editor”角色更新为“Contributor”:
const request = store.openCursor();request.onsuccess = event => { const cursor = event.target.result; if (cursor) { if (cursor.value.role === 'Editor') { const updatedUser = { ...cursor.value, role: 'Contributor' }; cursor.update(updatedUser); } cursor.continue(); }};