重复原生链接导致 PDF 首屏与交互停顿¶
2026-10-29。此前的 512px 瓦片优化只验证了 PDF 字节的出图路径;没有加载实际 Vault 的批注 sidecar,未覆盖这里的根因。
复现与原因¶
完整桌面应用打开 AgentCompass(papers/2607.13705),首屏长时间空白,框选和其他交互也可能等在大量后台任务之后。测试使用原 Vault 的本地副本,PDF、批注、版面与笔记均保留,SQLite 用只读连接备份;未改动原 Vault。
这份样本的 marks/annotations.json 为 17,757,601 bytes,共 19,808 条:
- 19,800 条 LINK,除顶层 id 外,实际仅有 198 种内容,每种恰好重复 100 次。
- 8 条用户 HIGHLIGHT。
- PDFium 直接读取原 PDF,得到 198 条原生 LINK;它们的完整内容与这 19,800 条 LINK 匹配。
PDFium 为没有 /NM 的原生链接生成临时 UUID;当前引擎把它写入内存中的 PDF。应用导出全部批注到 sidecar,重开原始 PDF 后原生链接又获得新 id。旧实现仅按 id 去重,随后把 sidecar 逐项交给 importAnnotations。上游每项 dispatch 创建状态,最后 auto-commit 又为每项提交 createPageAnnotation。重复项因此增加了主线程状态复制、排序、DOM 链接命中层和引擎排队。已证实本样本存在这种重复;没有把当前 100 份的历史来源当成已追溯完成。
修复边界¶
annotation-items.ts 统一处理读取、保存、首次导入和文件监听重导入的判重:
- 保留原有 id 判重。
- 仅对没有 transfer context 的 LINK,忽略顶层瞬态 id,按其余完整内容生成稳定键。嵌套目标 id、页码、位置、颜色、flags、备注、日期、custom 等仍参与比较,JSON 属性顺序不影响结果。
- 导入时同时比较当前引擎已加载的批注,跳过已有原生链接。
- 不按内容合并普通高亮;带 transfer context 的记录保留,避免把二进制上下文当成空 JSON 后误合并。
读取旧文件只在内存中压缩,不在打开时自动重写。后续正常保存使用同一规则,避免链接副本继续增长。源 PDF 不重写。
实测¶
macOS 26.6.2 / WebKit,React 19.2.7、EmbedPDF 2.14.4,完整 Tauri 开发应用,真实 PDF + NOTES 分屏,uiScale=1、缩放适应宽度约 89.2%、DPR=2、本机 CJK 字体 fallback。基线为 0aa309003,修复条件只加入本次批注判重;512px 瓦片保持一致。
通过临时诊断记录引擎任务 call / enqueue / start / done 和页面图片 load、rAF 长帧;临时采集代码不提交。计时从 openDocumentBuffer 发起开始,图片 load 包括其加载与解码,不等同于 GPU 实际呈现时刻。以下为一次完整原生对照,不能当作所有 PDF 的固定耗时:
| 指标 | 修复前 | 仅批注判重后 |
|---|---|---|
| 首张 PDF 图片 load | 48,048ms | 526ms |
| 首次 sidecar 创建任务 | 19,808 | 8 |
| 主线程长帧 | 观察到 41,661ms | 首屏阶段最大约 156ms |
| 第 3 页框选裁剪 | 大量任务队列持续数分钟,未取得可比裁剪值 | PNG 裁剪 Task 176ms |
修复后通过原生鼠标框选第 3 页插图,出现「批注备注」编辑器;原有高亮、页边备注与原生链接仍显示。框选耗时是裁剪任务值,不包含用户拖动时间和编辑器持久化完成时间,没有虚构修复前裁剪速度比。
只读 Node 调用同一判重函数,将真实 19,808 条压成 206 条约需 99ms,8 条高亮逐条保留。再与 198 条原生链接比较,实际只需导入 8 条高亮。
Chromium 实际阅读器加载同一批注 sidecar 后,经原生键盘事件编辑备注并提交,保存为 206 条(198 LINK + 8 HIGHLIGHT);卸载并重开阅读器后,8 条高亮与新备注仍保留。浏览器使用批注存储的既有内存 fallback,这部分不冒充 Host 文件写入验收。
回归测试覆盖属性顺序、重复 id、不同链接目标/位置/样式/备注/custom、二进制 transfer context、重叠用户高亮,以及连续导出重开后条目数不再增长。
补充验收与后续¶
解锁后的完整原生流程已补齐:实际编辑高亮备注、框选第 3 页、输入中文备注,Host 保存了高亮 sidecar、visual JSON 与裁剪 PNG。退出并重开后两类备注及裁剪图恢复,保存后的 sidecar 仍为 198 LINK + 8 HIGHLIGHT。逐项核对 8 条高亮的 id、几何、custom、颜色与原件一致,其余 7 条备注原文保留;原 PDF 和原批注文件哈希未变。调度的三轮原生对照另见可见页面优先性能对照。
已检查开发文档,仓库无独立 Roadmap / TODO。本问题已使用完整 Vault 副本验收首次打开、框选、批注保存与重开、链接跳转和连续切页;后续回归也应保留这一完整输入,只加载 PDF 字节会漏掉 sidecar 引起的性能问题。合法的大量不同批注仍可能触及上游逐项导入的成本,不能通过内容去重解决;如再复现,应单独测量批量导入调度,不能删除不同内容的批注来换取速度。