最近用过的 6 个标签保持挂载、隐藏而非销毁,切回零解析零序列化、各窗格 保留滚动位置;超限窗格卸载走最终回写。引用改为按标签注册表 + 活动变化 effect 统一指向;外部重载 epoch 按标签粒度、抑制闸门对所有标签生效。 EditorPane 新增就绪状态:首次打开大文件显示「正在打开文档…」加载态。 打印守卫:只出活动窗格。vitest 87 通过 + 2 跳过(新增 pane-loading 1 例)。
2.4 KiB
2.4 KiB
标签切换保活与加载状态(2026-11-14)
用户反馈
切换大文件时要等很久才能切回去,希望立即切回;至少要有加载进度/状态。
根因
原架构下每次切标签都销毁旧编辑器(全量序列化回写)并为新标签从 Markdown 全量解析重建编辑器。阶段 6 实测(jsdom 悲观口径):626K 字符建模 29.8s、 序列化 2.6s——大文档来回切换就是把这个成本反复付两遍。
改造:编辑器保活
- App 改为按标签渲染多个编辑器窗格,只有活动窗格可见,其余
display:none保持挂载:切回时零解析、零序列化,立即显示;每个窗格自带滚动容器, 连滚动位置都各自保留。 - 保活上限:最近用过的 6 个标签(LRU)。超出的窗格卸载,卸载前走既有的 最终回写(syncEditorContent),内容不丢,之后再切到时按状态内容重建。
- 编辑器引用改为注册表(paneApisRef: tabId → api);onReady 只登记, 共享引用(查找/大纲跳转/源码锚点)由「活动标签变化」effect 统一指向 活动窗格,后台窗格就绪/卸载不会再冲掉活动窗格的引用。
- 外部重载(reloadTabFromDisk)的 epoch 重挂改为按标签粒度(paneEpochs), 且闸门(抑制卸载回写)对所有标签生效——保活后非活动标签也可能正挂载, 旧实现只处理活动标签会漏。退出源码模式的重建同理改为按标签 bump。
- 状态栏光标只接受活动窗格的回调,后台窗格不再干扰。
加载状态
- EditorPane 新增就绪状态:编辑器 create() 完成前在窗格内显示 「正在打开文档…」+ 旋转指示(.pane-loading),就绪即撤下。 首次打开大文件时标签立刻出现、窗格明确处于加载中,不再是无反馈的卡住。 解析是单线程同步计算,做不了真实百分比进度,用明确的加载态代替。
打印
@media print 增加守卫:.tab-pane 打印时隐藏、仅 .active 出纸, 避免保活窗格被通用 display:block 规则一起打出来。
验证
- tsc 0 错误;vitest 87 通过 + 2 跳过(新增 tests/pane-loading.test.tsx: 创建未完成时显示加载提示、完成后撤下并回传就绪 API);构建通过。
- 待真机验:两个大文档来回切换应立即呈现;首次打开大文档有加载态; 外部修改重载、源码模式进出、打印只出当前文档。