>6 万字符文档按顶层结构安全切分:第一片立即建编辑器,其余片逐片解析 追加、片间让出主线程,窗格左下角显示载入进度;载入期间可切标签、编辑 其他文档。追加期静音回写、状态保持磁盘完整原文,载完一次性对齐。 分片与整篇解析的序列化等价有测试锁定(tests/chunk-load.test.ts)。 此前试做的已关闭重开缓存已按用户要求整笔撤回。
49 lines
3.3 KiB
Markdown
49 lines
3.3 KiB
Markdown
# 大文档异步载入(2026-11-14,取代已撤回的「重开缓存」方案)
|
||
|
||
## 需求(用户口径)
|
||
|
||
大文件打开慢可以接受,但不能阻塞其他操作:打开要在后台进行,给出加载中
|
||
状态,用户能切标签、编辑别的文档、点菜单。
|
||
|
||
## 为什么不是真 Web Worker 线程
|
||
|
||
实测探针:Milkdown 的解析管线离不开 DOM——headless Editor 在 Node 里
|
||
create() 直接抛 `document is not defined`(core 的 editorView 插件在装配期
|
||
就引用 document.body,其后 ProseMirror 视图构建也需要真实 DOM)。
|
||
把整套 DOM 模拟进 Worker 不可靠,故采用主线程分片让行达到同样的
|
||
「不阻塞」效果:每片工作控制在几十毫秒量级,片间让出主线程。
|
||
|
||
## 方案(src/editor/chunkLoader.ts + EditorPane)
|
||
|
||
- 阈值:内容 > 60,000 字符且可切分时走分片;否则维持同步打开。
|
||
- 切分(splitMarkdownChunks):围栏感知扫描,只在「空行 + ATX 标题 /
|
||
围栏开始 / 分隔线」处切——这些位置之前的块必然已闭合,片段独立解析
|
||
与整篇解析等价(等价性有自动化测试锁定);按约 24,000 字符攒片。
|
||
文档含链接引用定义或脚注定义时不切(定义可位于文末被前文引用),
|
||
整篇一片同步解析(与此前行为一致)。
|
||
- 载入:第一片直接建编辑器(马上能看、能滚);其余片借一台隐藏的同厂
|
||
编辑器取 parserCtx 逐片解析,经 JSON 转交给目标编辑器(两台编辑器的
|
||
schema 是不同实例,直接插节点会被 ProseMirror 静默丢弃——实测踩过,
|
||
已加「追加未生效即抛错」的断言),追加到文末(不进撤销历史),每片后
|
||
setTimeout(0) 让行并更新进度。
|
||
- 进度:窗格左下角徽标「正在载入文档… N%(可切换到其他文档继续工作)」,
|
||
不遮挡已载入内容;切到别的标签时它在隐藏窗格里继续载完。
|
||
- 状态一致性:追加期间静音序列化回写,标签状态里始终是磁盘上的完整
|
||
原文(自动保存/恢复/外部变更检测都不受影响);载完一次性以最终序列化
|
||
对齐(不标脏)。载入中编辑已载入部分是允许的,最终对齐会把编辑一并
|
||
收进去。若某片解析/追加失败:解除静音、把已载入部分如实回写为
|
||
未保存状态,绝不冒充完整文档。
|
||
|
||
## 验证
|
||
|
||
- tests/chunk-load.test.ts 5 例:切分拼接逐字还原、围栏内假标题不切、
|
||
引用定义整篇一片、小文档不分片;66K 字符富结构文档(标题/宽松列表/
|
||
任务/表格/围栏/Mermaid/公式/引用/分隔线)分片载入的序列化与整篇解析
|
||
**逐字相等**。全量 vitest 94 通过 + 2 跳过;tsc 0;构建通过。
|
||
- 无头浏览器实测(596K 字符文档):徽标从 13% 起显示;载入中真实鼠标
|
||
点击切到其他标签立即生效(徽标在其隐藏窗格里继续涨到 22%…),在其
|
||
编辑器里打字实时落字;全程无页面错误;载完 180 个二级标题齐全。
|
||
总耗时约 10.6 秒为本机无头慢速口径,WebView2 真机应明显更快;
|
||
关键不在总时长,而在全程界面可操作。
|
||
- 待真机验:桌面模式打开 100K+ 文档时的进度徽标与切换手感。
|