cursor.continue()实现分页的核心是游标递进定位而非跳过前n条,通过lastkey参数seek到指定键或更大键的下一条记录,配合索引顺序(如倒序)实现高效“下一页”加载,避免循环调用导致性能问题。

用 cursor.continue() 实现本地海量数据的分页查询加载,核心是利用 IndexedDB 的游标(cursor)遍历能力,配合游标定位和分页控制,避免一次性读取全部数据导致内存溢出或卡顿。关键不在“跳过前 N 条”,而在于“从某条记录开始继续遍历”。
理解 cursor.continue() 的本质作用
cursor.continue(key) 不是“跳过前 N 条”,而是让当前游标移动到键值 ≥ key 的下一条记录(按索引顺序)。若不传参,就移动到下一条;若传入具体 key,则“seek”到该 key 或第一个大于它的记录。
这意味着:它天然适合“下一页”加载,但不适合传统 SQL 的 OFFSET 式跳页(比如直接跳到第 10000 页)。分页必须是**连续、递进式**的。
实现“下一页”加载的典型流程
以按时间倒序展示日志为例(索引为 timestamp,降序):
- 首次查询:用
openCursor(null, 'prev')打开倒序游标,取前 20 条,并记下最后一条的timestamp(即最小时间戳) - 加载下一页:调用
cursor.continue(lastTimestamp),游标会定位到timestamp ≤ lastTimestamp的下一条(因是降序,实际是更早的一条) - 重复取 20 条,更新 lastTimestamp,继续调用
continue()
⚠️ 注意:如果存在相同 timestamp 的多条记录,需额外用主键(如 id)做二级排序或去重处理,否则可能漏数据或重复。
避免常见陷阱的操作要点
不要用 cursor.continue() 模拟 OFFSET:比如想跳到第 1000 条,就循环调用 999 次 continue() —— 这会严重拖慢性能,且无法中断。
推荐替代方案:
- 用 ID 范围分页:预先统计总数,按主键区间切分(如 id ∈ [1000, 1019]),用
IDBKeyRange.bound()查询 - 用时间窗口分页:对时间字段建索引,按“昨天”、“前天”等自然窗口加载,再在窗口内用
continue()细分 - 结合游标 + 缓存偏移量:首次遍历时缓存每 100 条的 cursor.key,后续跳页直接 seek 到对应 key,再 continue
一个简洁可用的分页加载函数示例
(假设已打开 objectStore,索引名 'byTime',降序)
async function loadNextPage(lastKey = null, limit = 20) {
return new Promise((resolve, reject) => {
const tx = db.transaction('logs', 'readonly');
const store = tx.objectStore('logs');
const index = store.index('byTime');
const range = lastKey ? IDBKeyRange.upperBound(lastKey, true) : null;
const req = index.openCursor(range, 'prev');
req.onsuccess = () => {
const cursor = req.result;
if (!cursor) return resolve({ data: [], hasNext: false });
const data = [];
let count = 0;
do {
data.push(cursor.value);
count++;
if (count >= limit) break;
cursor.continue(); // 移动到下一条(更早的时间)
} while (cursor && count reject(req.error);
});
}
调用方式:loadNextPage() → 得到第一页;拿到返回的 nextStartKey 后,再传入调用即得下一页。










