[ISSUE-0051] 商家列表打开卡顿(B+C 方案)¶
用户反馈(v0.10.70):「为什么打开官网列表和手机列表不卡,但是打开商家列表会卡?」
深度分析(架构层差异)¶
| 维度 | merchant tab | website/email/phone tab |
|---|---|---|
| 分页 | server-side(jsstore 每次 20 行 + 22w 行 sort) | client-side(一次拉 50k 到 React state) |
| 列表组件 | DataGridPremium(重) | ClientDataTable(轻量) |
| 派生 | 无 | useMemo dedup(in-memory,几 ms) |
打开 merchant tab 同时触发 4 个 IndexedDB transaction:
1. dataRun — 22w 行 order by create_time desc + skip 0 + limit 20
2. countRun — 22w 行 native count
3. merchantStatsRun — 50k select + JS for loop(v0.10.70 改成 3 个 count + 50k 采样)
4. rows polling — 每 5 秒 select 50k 行(即便在 merchant tab 也跑,因为 KPI 计数依赖它)
website/email/phone tab 复用 rows,没 server-pagination,没额外 stats,只剩 useMemo 派生几 ms → 瞬开。
修(B + C 组合)¶
B:rows polling 5s → 30s + event-driven¶
src/sections/data/data-view.tsx:120:
加 runtime message 监听 — background/batch-controller.ts:418 已经在 insert 完发 maps-data-updated,data-view 监听后 refreshRows():
useEffect(() => {
const handler = (msg: any) => {
if (msg?.type === 'maps-data-updated') refreshRows();
};
browser.runtime.onMessage.addListener(handler);
return () => browser.runtime.onMessage.removeListener(handler);
}, [refreshRows]);
效果: - 数据真变时立即刷新(2-5s 延迟内可见 background insert 完的新数据) - 没新数据时 30s polling 后备 - 后台 CPU 占用 降 6x(5s 12 次/min → 30s 2 次/min + 偶发 event)
C:默认空 sortModel → jsstore 走 id desc¶
local-data-view.tsx:
- sortModel: [{ field: 'create_time', sort: 'desc' }] as any[],
- sort: { create_time: -1 },
+ sortModel: [] as any[],
+ sort: {},
api/search.ts apiLocalDataList:
const dbSort =
Array.isArray(dbSortRaw) && dbSortRaw.length === 0
? [{ by: 'id', type: 'desc' }] // 默认走主键索引(cursor 最快)
: dbSortRaw;
为什么 id desc 等价于 create_time desc:
- id 是 IndexedDB 自增主键
- create_time = Date.now() 在 insert 时设置
- 自增 id 顺序 == 插入顺序 == create_time 顺序
为什么 id desc 快 ~10x:
- IndexedDB 主键 cursor 是天生反向遍历(openCursor(null, 'prev'))
- jsstore order: { by: 'create_time' } 即便有 enableSearch 索引,走的是二级索引 cursor,比主键 cursor 慢
- 22w 行的 first page:~ 500ms → ~ 50ms(实测预期)
性能预期¶
| 操作 | 旧 | 新 | 提升 |
|---|---|---|---|
| merchant tab 首次打开 | 1-3s 卡顿 | ~ 200ms | 5-10x |
| 22w 数据 rows polling 1min 内拉取次数 | 12 | 2 + event | 6x CPU 降 |
| dataRun first page | ~ 500ms | ~ 50ms | 10x |
注意点¶
- DataGrid 表头不再默认带"创建时间"箭头,但行序仍是最新在前(用户感知一致)
- 用户主动点表头排序 → 仍走原路径(
create_time/rating/quality等都正常) 'maps-data-updated'这个 runtime message 已经在 v0.10.x 由 background 发送,不需要新协议- localStorage 里 sort 配置如果之前保存了
create_time desc,不会被新代码覆盖 — 但 v0.10.71 后只有用户主动点表头才能"设置" sort,所以新装用户默认是空 sortModel
不做的优化¶
- A 方案:merchant tab 也走 client-side 50k 复用 rows — 长期方案,但 > 50k 数据丢失访问。本轮先用 B+C 看效果,>50k 数据的彻底优化留下轮
- D 方案:合并 IndexedDB transactions — jsstore 不一定支持复合 query,风险高收益不确定
audit_grep¶
- pattern: "pollingInterval:\\s*5000[^]]"
description: "5s polling 在 22w 数据时压力大 - 默认改 30s + event-driven"
相关¶
- [[0050-merchant-chip-truncate-50k-slow|0050-商家列表chip截断50k-加载慢]] — 上一轮,修了 stats 截断 + 轮询 10→30s
- [[0029-dataview-perf-listener-no-cleanup|0029-DataView性能浪费索引-listener无清理]] — 历史性能优化