Slate v2 通用大文档性能架构:从 Two-Tier Auto 策略到 5000 块默认性能门禁的完整设计
2026/9/17 2:30:25 网站建设 项目流程

Slate v2 通用大文档性能架构:从 Two-Tier Auto 策略到 5000 块默认性能门禁的完整设计

【免费下载链接】plateRich-text editor with AI and shadcn/ui项目地址: https://gitcode.com/GitHub_Trending/pl/plate

导读

本文围绕 Slate v2 重写(当前 plate 仓库中packages/slate及配套docs/slate-v2文档体系所指向的下一代编辑器内核)在大文档场景下的性能架构展开,核心是 docs/plans/2026-05-01-slate-v2-universal-large-document-performance-ralplan.md 这份已被执行的 ralplan 文档。文中完整继承该计划提出的largeDocument公共 API 形态、EditorCommit脏数据模型、DOM-present 根分组、sidecar 范围索引、shell 显式模式,以及 5000/10000 块两级基准门禁;并结合当前仓库的 chunking-review.md、master-roadmap.md、absolute-architecture-release-claim.md 等配套文档与执行台账,还原从「停止声称胜利」到「默认性能门禁关闭」的完整决策链与可验证证据。读完本文,你将掌握 Slate v2 大文档性能问题的完整问题定义、双层默认策略(two-tier auto)的取舍逻辑、脏提交(dirty commit)契约的字段语义,以及如何用interactiveReadynativeSurfaceComplete双指标、命名化基准面与浏览器证明门禁来理性验收「更快」。

1. 问题定义:为什么大文档性能需要一个独立架构计划

Slate v2 重写的目标不是「看起来更干净」,而是在真实编辑器负载上击败 legacy Slate。ralplan 的 Intent / Boundary Record 明确写道:意图是让 Slate v2 重写在有意义的编辑器工作负载上胜过 legacy Slate 性能,期望产出是定义一种默认大文档模式的架构与证明门禁,使其在打字(typing)上达到或超过 legacy chunking,同时保留 v2 在启动与整文档操作上的优势。

该计划被标记为status: closed-with-follow-up,最新评审分0.88,执行状态为active execution。其范围覆盖公共大文档 API、内部分组策略、提交脏数据、React 订阅、sidecar 装饰/标注/组件、选择与 DOM 桥、shell 岛策略、基准矩阵、无障碍、浏览器证明,以及 Plate 与 slate-yjs 的迁移骨干。明确非目标包括:在本 pass 内实施计划、保留当前 shell 选项命名、产品级评论服务、当前版本 Plate 或 slate-yjs 适配器兼容、把 raw Slate 变成产品编辑器。

当前仓库中 chunking-review.md 是这份计划的重要佐证:它把 legacy 的 child-count chunking 定性为「历史上有用的优化」与「对照基线,而非设计约束」,并指出 v2 的正确路线是「先修局部订阅,再做语义岛,最后才考虑类似 chunk 的次级优化」,这与 ralplan「偷走 chunking 的分组/记忆化机制、杀掉公共 chunking API」的决策完全一致。

2. 关键结论与决策演进:shell 从默认架构降级为显式逃生舱

2.1 Current Verdict:shell 真实了,但还不是默认胜利

在最新一次执行切片后,计划给出了诚实的分层结论:

  • shell 显式模式成为可信的显式逃生舱:显式mode: 'shell'、挂载走廊语义、组合(composition)守卫、shell 支撑的选择/复制契约、脏提交载荷、绿色 shell 冒烟车道都已落地,这是真实改进。
  • DOM-present / 默认路径不再是打字失败者:交互就绪(interactive ready)、稳定中段打字、全选、可见整文档替换、可见片段插入均已在 5000 块中位数上击败 legacy chunk-on。
  • nativeSurfaceComplete单独度量:在 5000 块下仍约 1 秒,必须独立报告,不能藏在交互就绪里。
  • 遗留红色 owner 是 10000 块压力形状:selection-inclusive 与 promote 车道扩展性仍差,尽管 ready 与可见整文档操作已经碾压 legacy。

基准真值修正是本计划的一个重要动作:旧的*NativeBeforeInputTypeMs行派发的是@字符,运行时正确地将其路由到 Slate 模型拥有的 beforeinput 路径——因此那些行不是真实浏览器插入证明,现已重命名为*SelectThenModelBeforeInputTypeMs

2.2 最终决策:two-tier auto

经过 GPT Pro 后续评审,被采纳的公共默认策略是:

// Public default <Editable /> // Optional policy override largeDocument?: | 'auto' | 'dom-present' | 'off' | 'shell' | { mode: 'shell' activeRadius?: number islandSize?: number previewChars?: number threshold?: number }

默认auto是两层结构:

  • Layer 1(安全 DOM-present 基线):分组、scoped 订阅、active corridor、containment、经证明处使用content-visibility、安全处直接 DOM 文本同步、持久 sidecar 范围索引。
  • Layer 2(激进 shell/遮挡升级):在浏览器 find、屏幕阅读器、原生选择、复制/粘贴、IME、移动端、撤销、历史、协作证明全部通过之前保持「证明禁用」;在此之前 shell 虚拟化必须通过mode: 'shell'显式开启。

新硬性规则:默认auto只有在分别跟踪interactiveReadynativeSurfaceComplete,且整文档变更后绝不把陈旧远端 DOM 暴露为当前内容时,才允许使用分阶段的 DOM-present 挂载。

2.3 备选方案评估与拒绝理由

决策简报给出了五个备选方案的对比:

方案判定理由
显式 opt-in shell 模式拒绝作默认,保留为逃生舱可安全实验发布,但不符合「默认更快」,且重复 legacy 调参气味
通用 shell 岛拒绝原生/a11y 风险巨大,当前打字车道糟糕
通用 DOM-present auto部分接受最安全默认,但仍需激进 shell 选项应对巨大研究型文档
双层 auto(two-tier auto)采纳安全默认 + 显式激进模式,与证据吻合
去掉岛、只修脏运行时拒绝只攻击打字,却丢掉了已被证明的启动/整文档优势

被拒绝的备选方案清单(Hard Cuts 部分)还包括:保留当前显式 island 标志(DX 调参过多、默认不快)、直接照搬 VS Code 视口渲染(在未自拥输入/a11y 栈时不成立)、照搬 Lexical DOM reconciler(除非 Slate 放弃 React body 渲染)、照搬 ProseMirror 整数位置(不符合 Slate 公共模型)。

3. 公共 API 目标:cutenabled,政策形状先行

计划明确要求砍掉误导性的largeDocument.enabled布尔形态。默认形态是:

<Editable />

可选覆盖:

type LargeDocumentOptions = | "auto" | "dom-present" | "off" | "shell" | { mode: "shell"; activeRadius?: number; islandSize?: number; previewChars?: number; threshold?: number; };

规则要点:

  • auto:默认,DOM-present 优先;
  • dom-present:强制安全的可编辑分组 DOM;
  • shell:显式激进模式;
  • off:诊断逃生舱(真实的无分组控制行);
  • 文档中不出现公共enabled布尔;
  • 非 shell 模式不泄漏 shell 旋钮;
  • 文档更推荐{ mode: 'shell' }对象形态,因为对象形态让激进选择可见;
  • 基准/调试行必须命名实际模式,而不是写「无岛」。

4. 内部运行时目标:脏提交、选择器扇出、根分组、走廊、范围索引与选择桥

4.1 Commit Dirtiness:让核心知道「到底改了什么」

现状是EditorCommit已有有用字段但缺少顶层/分组特定与 DOM 同步特定的证明字段。目标形态:

type EditorCommit = { // existing fields kept dirtyTextRuntimeIds: readonly RuntimeId[] | null; dirtyElementRuntimeIds: readonly RuntimeId[] | null; dirtyTopLevelRuntimeIds: readonly RuntimeId[] | null; dirtyTopLevelRanges: readonly TopLevelRuntimeRange[] | null; affectedTextRuntimeIds: readonly RuntimeId[] | null; affectedNodeRuntimeIds: readonly RuntimeId[] | null; affectedProjectionRuntimeIds: readonly RuntimeId[] | null; affectedSelectionRuntimeIds: readonly RuntimeId[] | null; structuralDirtyRuntimeIds: readonly RuntimeId[] | null; textDirtyRuntimeIds: readonly RuntimeId[] | null; markDirtyRuntimeIds: readonly RuntimeId[] | null; rootRuntimeIdsChanged: boolean; topLevelOrderChanged: boolean; fullDocumentChanged: boolean; };

一次简单insert_text的理想结果:

textDirtyRuntimeIds = [textId]; dirtyElementRuntimeIds = [paragraphId]; dirtyTopLevelRuntimeIds = [paragraphId]; dirtyTopLevelRanges = [[index, index]]; rootRuntimeIdsChanged = false; topLevelOrderChanged = false; fullDocumentChanged = false;

实现注意:核心提交保持渲染器无关且冻结。DOM 文本同步是slate-react的能力/结果,不进入核心EditorCommit字段;若 Phase 2 需要按运行时 DOM 同步遥测,应加到 React selector/change context,而不是packages/slate

4.2 运行时选择器扇出:从广播到按运行时 id 定向

现状useEditorSelectorContext把每个提交广播给每个 selector 监听器,运行时选择器在本地用shouldUpdate过滤。目标:

  • 运行时 id 选择器注册进按运行时索引的监听器 map;
  • affectedNodeRuntimeIds命中的提交只通知这些运行时 id;
  • affectedNodeRuntimeIds: null的宽提交仍安全扇出;
  • 已同步文本的提交可借助既有操作策略跳过挂载渲染选择器;
  • 普通应用代码继续使用useNodeSelector/useTextSelector
  • 底层useEditorSelector的运行时 id 选项仅供运行时自有选择器与测试。

硬规则:

  • 纯文本操作不得重建根运行时 id;
  • 纯文本操作不得重建分组/岛计划;
  • 纯选择提交不得唤醒正文文本订阅者;
  • DOM 文本同步成功必须抑制挂载文本重渲染;
  • projection 重算必须接收受影响的运行时 id,而不是笼统的「发生了文本」。

4.3 DOM-present 根分组:偷走机制、杀掉 API

legacy chunking 通过记忆化 chunk 取胜,v2 shell 岛通过不挂载大多数内容赢得启动但输掉打字。DOM-present 根分组目标:

EditableRoot SlateRootGroup range=[0, 999] SlateRootGroup range=[1000, 1999] SlateRootGroup range=[2000, 2999]

每个分组:

  • 按顶层范围订阅;
  • 记忆化稳定的渲染回调;
  • 保留真实可编辑 DOM 后代;
  • 仅在证明后才使用 containment 与content-visibility
  • 仅当提交范围与分组范围重叠时重渲染。

更新判定:

shouldGroupUpdate(groupRange, commit) { return commit.fullDocumentChanged || commit.topLevelOrderChanged || rangesOverlap(groupRange, commit.dirtyTopLevelRanges) || rangesOverlap(groupRange, commit.selectionTopLevelRanges) || rangesOverlap(groupRange, commit.projectionTopLevelRanges) }

关键纪律:不暴露renderChunkgetChunkSize。当前仓库的 chunking-review.md 详细记录了「arbitrary 1000 children per chunk 是钝的,不懂编辑语义」这一判断,主张语义岛(顶层块、section 子树、表格子树、void/嵌入块)优先于任意计数。

4.4 Active Corridor:active 意味着已挂载

现状activeRadius具有误导性——相邻岛可能被标记 active,却只有一个 active 岛挂载运行时 id。目标:

  • DOM-present 默认在选区周围使用真实可编辑分组走廊;
  • 推荐默认证明候选是当前组加前后各2组;
  • shell 模式挂载走廊组、仅远处组用 shell;
  • active 意味着已挂载,否则改名

4.5 Sidecar Range Index:让标注/投影变成 O(受影响范围)

现状 projection/annotation 存储方向正确,但大文档证明必须验证 source-scoped 重算与运行时桶局部性。目标:

type ProjectionRangeIndex = { byRuntimeId: Map<RuntimeId, ProjectionSlice[]>; byTopLevelRuntimeId: Map<RuntimeId, RuntimeId[]>; intervalTreeByTopLevel: Map<RuntimeId, IntervalTree<ProjectionSlice>>; sourceVersions: Map<SourceId, number>; mappedThroughVersion: number; };

规则:

  • 只映射/重基(map/rebase)受影响锚点;
  • 只重算受影响运行时桶;
  • 只唤醒已挂载的受影响运行时 id;
  • 评论元数据更新不重绘内联文本,除非 projection 载荷变化;
  • 默认无全局decoratepass。

这一方向的仓库佐证同样在 decorations-annotations-cluster.md 与 decoration-roadmap.md 中,后者记录了 sidecar 标注/投影作为 v2 装饰体系核心的演进。

4.6 Selection Bridge:一个 ProseMirror 式的选择/观察者 owner

新增单一 owner:

  • 一个 DOM observer;
  • 围绕 DOM 写入做受控断开/重连;
  • DOM 选择导入;
  • Slate 选择导出;
  • 组合安全模式;
  • 陈旧目标与远端编辑的选择书签(selection bookmarks);
  • shell 支撑选择显式标记为 synthetic/model-backed。

5. Hook / Component / Render DX 目标

  • React hooks 保持 selector-first 与 commit-gated;
  • 外部订阅采用useSyncExternalStore姿态;
  • 可见打字保持紧急,侧栏、overlay 与非编辑 UI 延后;
  • 不强迫应用作者提供 chunk wrapper;
  • 不在 raw Slate 暴露产品 UI;
  • 继续支持自定义renderLeafrenderSegmentrenderText,但因 DOM 同步对这些禁用,需基准化回退打字车道;
  • 扩展写入保持在tx组下;
  • 命令可发现性作为元数据提供,而非公共扩展命令变更槽:
defineEditorExtension({ name: 'comments', tx: { comments: ... }, capabilities: { commands: [ { id: 'comments.add', title: 'Add comment', tx: 'comments.add', shortcut: 'Mod-Alt-M', }, ], }, })

6. Plate 与 slate-yjs 迁移骨干

6.1 Plate Migration-Backbone

Plate 应迁移到:

  • raw Slatestate/tx扩展命名空间;
  • 供命令面板、菜单、工具栏使用的 capability 元数据;
  • 供评论/审阅使用的外部标注通道;
  • 供 Plate chrome 使用的 widget/overlay 通道;
  • 能支撑评论锚点的选择/书签 API;
  • raw Slate 内不做产品评论服务;
  • raw Slate 不要求editor.api/editor.tf兼容。

证明采用两条车道(two-lane)的 Plate 式评论示例:

  • 写者编辑文档值;
  • 审阅者对文档值只读,但通过 sidecar 标注通道添加评论;
  • 仅评论的更新不改变 Slate value;
  • 评论元数据更新唤醒侧栏但不重绘文本。

6.2 slate-yjs Migration-Backbone

协作需要确定性基板,而非当前适配器支持。要求:

  • 操作保持确定性与可重放;
  • 远端应用产生相同的提交元数据类;
  • 运行时 id 与持久锚点在文档编辑中存活;
  • Yjs relative positions 可实现通用标注锚点契约;
  • 仅评论更新可通过 sidecar Yjs doc/map 同步,无需写入 Slate value;
  • 陈旧目标解析需覆盖光标前远端插入/删除与含锚点删除的证明。

7. 基准矩阵:尺寸、模式、分组、走廊、车道与 5000/10000 门禁

7.1 文档尺寸与模式

文档尺寸:

BlocksPurpose
100基线开销
250小型真实文档
500中型文档
1000chunk 阈值 sanity
2000smoke/debug 门禁
5000主发布门禁
10000stress/nightly 门禁
25000仅 shell/viewport 研究
50000仅非默认激进研究

模式:legacyChunkOfflegacyChunkOnv2Offv2DefaultOmittedv2AutoExplicitv2DomPresentv2ShellExplicitRadius0v2ShellExplicitRadius1v2ShellAutoCandidate(仅证明后)、v2CustomLeafv2ProjectionHeavyv2AnnotationsHeavyv2CollabRemoteEdits

7.2 分组大小、走廊与必需车道

  • DOM-present 分组:10025050010002000
  • Shell 分组:501002505001000
  • 走廊:0+/-1 group+/-2 groups+/-5 groupsviewport + overscan

必需车道:ready、first paint、first selection、1/10/100 文本操作的稳定打字、原生beforeinput打字、select-then-type、shell 专用 promote-then-type、select-all、复制整文档、粘贴整文档、替换整文档、插入整文档片段、undo/redo、composition、光标前远端插入、含锚点远端删除、projection 刷新、标注元数据更新、浏览器 find、屏幕阅读器路径、Android Chrome 打字、iOS Safari 打字。

7.3 5000 块与 10000 块门禁

5000 块门禁:

  • ready:v2 auto <= legacy chunk-on(理想 3 倍快);
  • 稳定打字:v2 auto <= legacy chunk-on +5 ms中位数;
  • select-then-type:v2 auto <= legacy chunk-on +10 ms中位数;
  • 整替换:v2 auto <= legacy chunk-on(理想 2 倍快);
  • selectAll:v2 auto <= legacy chunk-on +5 ms,或证明 synthetic select/copy/paste 对等;
  • projection 局部更新:O(受影响运行时 id),非 O(文档);
  • 标注元数据更新:无文档正文重渲染;
  • custom leaf 打字:无灾难性回退;
  • IME/mobile:零证明回归。

10000 块门禁:

  • ready:v2 auto 至少比 legacy chunk-on 快 5 倍;
  • 稳定打字:v2 auto <= legacy chunk-on +15 ms中位数;
  • 整替换:v2 auto 至少比 legacy chunk-on 快 2 倍。

硬规则:在 5000 块打字匹配或超过 legacy chunk-on(按上述阈值)之前,不允许任何性能优越性声明。

8. 浏览器压力 / 对等策略与 shell 证明补充

必需证明族:模型状态、模型选择、可见 DOM、DOM 选择/光标、焦点、提交元数据、trace、后续打字、复制/粘贴、select-all、undo/redo、IME/composition、浏览器 find、屏幕阅读器遍历、移动 Chrome、移动 Safari、协作陈旧目标重基。

Shell 模式附加证明:在非活动 shell 范围 find 文本、屏幕阅读器遍历非活动范围、跨 shell 边界原生拖拽选择、复制 shell 支撑选择的局部与整体、在 shell 支撑选择上粘贴、组合期间不提升 shell、持久光标 soak。

仓库中 replacement-gates-scoreboard.md 与 true-slate-rc-proof-ledger.md 记录了这些证明族的逐项状态,是本文档浏览器门禁在仓库内的延伸台账。

9. 实施阶段:从停止声称胜利到默认门禁关闭

计划定义了从 Phase 0 到 Phase 7 的完整执行路径,这里继承其骨架并标注关键产出:

Phase 0:停止声称胜利

标记声称 v2 击败 legacy 打字的陈旧文档为无效;更新基准报告展示当前失败车道;保持 shell 岛为实验性。门禁:基准表在 5000 与 10000 块下包含 ready、typing、select/type、select-all、full replace、insert fragment。

Phase 1:为打字添加 Commit Dirtiness

packages/slate增加 dirty text/element/top-level/runtime id 集合与 root/order/full-document 布尔;DOM 文本同步保持渲染器局部;纯文本操作不使 root/group 计划失效;纯选择提交不唤醒正文订阅者。门禁:简单文本插入提交证明一个文本 id、一个父元素 id、一个顶层范围、无 root 顺序变化;React provider 选择器收到这些 scoped 提交事实;Phase 1 不附任何 5000 块性能声明。

Phase 2:内部 DOM-present 分组(多轮迭代)

先削减选择器扇出(运行时 id 选择器走受影响运行时 id 路由),再加内部SlateRootGroup层,按顶层范围与提交重叠记忆化,保留真实可编辑 DOM 后代,仅在浏览器证明后加 containment/content-visibility,不暴露renderChunk/getChunkSize

执行台账记录了清晰的迭代曲线(均为 2000 块一次迭代冒烟,legacy chunk-on 作对照):

  • 根分组切片后:legacy chunk-on middle type33.07 msvs v2 shell78.23 msvs v2 no-island96.31 ms——策略本身不够;
  • 挂载子树插桩后:shell selector 事件100 -> 70,root 通知10 -> 0,但干净冒烟仍红(shell64.32 ms、no-island78.02 ms);
  • 选择/全局唤醒削减后:selection DOM export 不再为纯文本车道唤醒,但 shell62.91 ms、no-island71.22 ms仍红;
  • 原生 beforeinput 车道加入后:事件路径慢于直接插入,并每次操作唤醒 selection DOM export;
  • 事件输入选择导出削减后:beforeinputselection-dom-export-notify11降到1
  • 模型打字热路径插桩后:2000 块 profile 显示直接打字成本几乎全部在editor.update(tx.text.insert)内部(shell middle type60.18 ms中 update 占59.94 ms,provider DOM sync 仅0.03 ms);
  • 核心文本插入成本削减后:路径稳定文本快照复用前一索引、只克隆被编辑分支,回滚复用前快照子节点,折叠选择影响不再扫描每个索引路径——5000 块 shell 首次全绿(ready19.80 vs 301.66 ms、middle type23.72 vs 34.01 ms、replace17.10 vs 127.76 ms、fragment31.52 vs 114.60 ms),selectAll 与原生 beforeinput 仍红;
  • 原生输入与 selectAll 成本削减后:宽整文档选择影响不再物化每个选中运行时 id,合法 model-owned beforeinput 不再用Editor.string(editor, [])扫根——5000 块 shell explicit 通过 ready/selectAll/type/select-type/promote/full-doc 门禁,no-island 仍红(ready483.13 ms、native beforeinput332.93 ms、replace155.31 ms);
  • DOM-present 渲染子树削减后:no-island 首次通过 selectAll2.04 ms、middle type17.68 ms、middle select/type28.32 ms(legacy 对照1.3339.4151.50),但 ready、promote/type、native beforeinput、replace、fragment 仍红;
  • DOM-present 原生与整文档 owner 削减后:整文档提交发布 next live runtime ids 替代null,no-shell 5000 替换检查/通知从 10k/10k 降到 2/2,profile 显示整文档选择器扇出不再是瓶颈,剩余成本是挂载 DOM/React act flush;
  • beforeinput 车道拆分后:事件路径证据诚实化——shell model-beforeinput35.17 ms是 setup-free,旧 native 车道是 promote/setup-inclusive 且非真实浏览器插入证明;no-shell 事件输入/promote/ready/整文档仍红;
  • 默认策略重规划后:auto锁定为双层策略,DOM-present 为安全基线,shell/遮挡升级保持显式或证明禁用。

Phase 2 门禁:5000 块 ready 击败 legacy chunk-on;middle typing <= legacy chunk-on +5 ms;select-then-type <= legacy chunk-on +10 ms;full replace 在可行处保持至少 2 倍优势。

Phase 3:Source-Scoped Sidecar Range Index(已完成)

持久 projection 范围索引、锚点随操作映射/重基、声明 source dirtiness 与运行时范围、拆分 projection 载荷与标注元数据、增加两车道协作评论示例。门禁全部通过:写者编辑文档时只读审阅者添加评论;仅评论更新不改变 value;评论正文更新在 projection 不变时不重绘内联文本;大标注集保持运行时桶局部。

Phase 4:Selection Bridge 与 DOM Observer 纪律(本地浏览器证明完成)

一个 DOM observer/选择桥 owner;受控 DOM 写入时暂停 observer;DOM 选择映射到 Slate 选择;Slate 选择映射到 DOM 选择;组合安全模式;陈旧目标书签。执行台账显示:程序化整文档模型选择被分类为 shell-backed 而不扩展 shell;shell-backed 选择无法从 DOM 克隆时,剪贴板回退到 model-backed Slate fragment 载荷;clipboard.copyPayload()保持真实快捷键/OS 剪贴板,clipboard.copyEventPayload()是 shell-backed 选择的合成 copy-event 契约;挂载内容内的真实鼠标拖拽保持 native/DOM-owned;Chromium 原生组合与 Playwright mobile 合成组合均通过。Android/iOS 真实设备证明仍是发布/设备门禁,本 pass 不声称。

Phase 5:显式 Shell 模式

策略重命名为mode: 'shell';修复 active radius 使 active 岛挂载;一致推导 shell-backed 选择;完成 shell 复制/粘贴/select-all 语义;组合期间禁用提升(除非证明安全)。门禁:浏览器 find 非活动范围、屏幕阅读器遍历、跨 shell 边界原生选择、局部与整体复制/粘贴、IME/mobile/undo/collab/持久光标 soak。台账记录activeRadius修复后:半径0挂载完整 active 岛、半径1挂载相邻走廊岛、仅焦点交互不提升;5000 块一次迭代 shell explicit ready34.96 ms、selectAll0.36 ms、middle type10.03 ms,legacy chunk-on 对照为345.321.0331.00 ms

Phase 6:发布文档与示例(已完成)

文档化autooffdom-presentshell;提供大文档基准页;提供评论 sidecar 示例;提供 legacy chunking 用户迁移说明;文档保持「当前状态参考」而非变更日志散文。门禁:文档匹配 live API;示例通过浏览器证明;无陈旧「v2 击败 legacy」声明。

Phase 7:默认性能车道(GPT Pro 后续)

  • 7.0 基准面重命名与 trace 卫生v2LargeDocument->v2ShellExplicitRadiusNv2NoIsland->v2DefaultOmitted/v2AutoExplicit/v2DomPresent;新增v2Off等六行;trace 字段包含largeDocumentModegroupingEnabledshellEnabledgroupSizecorridorstagedMountingEnabledinteractiveReadyAtnativeSurfaceCompleteAtmountedGroupCountAtReadypendingGroupCountAtReadystaleGroupCountbackgroundMountChunksmaxBackgroundChunkMs
  • 7.1 DOM-present 整文档替换/片段生命周期:同步提交模型 -> 计算新根分组计划 -> 紧急挂载 active/viewport/corridor 组 -> 同步分离/清除旧远组 -> 调度新远组 -> 置nativeSurfaceComplete = false-> 全部刷新后置true。硬规则:旧远 DOM 可暂时缺失,但模型改变后绝不允许旧远 DOM 继续作为当前内容可见/可搜/可复制/可读。门禁验证整替换可见提交后旧远文本从 DOM 消失、新 active 文本立即出现、新远文本在nativeSurfaceComplete后出现、复制/选择使用 model-backed 载荷或新 DOM、5000 块整替换可见提交不高于 legacy chunk-on。
  • 7.2 DOM-present 启动分阶段:计算根分组 -> 紧急挂载 active/viewport/corridor -> 远组标pending-mount-> 报告interactiveReady-> 受限后台预算挂载远组 -> 报告nativeSurfaceComplete。门禁:interactiveReady不高于 legacy chunk-on ready;nativeSurfaceComplete单独度量;find/a11y/原生选择声明只在nativeSurfaceComplete后;持续打字/滚动不能无限饿死完成。
  • 7.3 分组大小、走廊与后台预算扫描:分组250/500/1000,走廊 viewport、viewport +/-1、viewport +/-2,后台预算4/8/16 ms;初始怀疑groupSize=500 | 1000corridor=viewport+/-1 | +/-2backgroundBudget=8 ms;不以100为默认除非扫描证明。
  • 7.4 生命周期后的 beforeinput/事件路径:逐段 profilebeforeinput received、目标 DOM 节点查找、DOM -> Slate point/range 导入、模型操作、规范化、提交脏数据、DOM 文本同步、选择导出/光标修复、React selector flush。门禁:setup-free model-owned beforeinput 在directModelType + small constant内;select/setup-inclusive 车道继续命名为 model-owned 而非 native;真实浏览器原生输入证明在 jsdom-only model-owned 车道之外单独添加。
  • 7.5 默认声明门禁:仅当 5000 块、5 次迭代中位数通过下表才允许默认性能声明。
区域要求
默认交互就绪击败 legacy chunk-on
默认直接/模型打字匹配或击败 legacy chunk-on
默认 beforeinput 打字<= legacy chunk-on + 10 ms
默认 select+type匹配或击败 legacy chunk-on
默认整替换可见提交匹配或击败 legacy chunk-on
陈旧 DOM证明不存在
nativeSurfaceComplete已度量且有界
sidecar 更新运行时桶 scoped
shell仍为显式

10. 5000 块门禁关闭:关键执行证据与 10000 块遗留 owner

Pass-State Ledger 完整记录了从 Phase 7 开始的量化证据链,最关键的几条(均为中位数对比):

  • Phase 7.5 确认前(分组 100,5 次迭代中位数):legacy chunk-on ready295.57 ms、middle type33.37 ms、middle select/type31.68 ms、replace115.05 ms、fragment111.26 msv2DomPresentready24.63 ms、native surface complete999.66 ms、selectAll0.19 ms、middle type8.51 ms、middle select/type38.31 ms、promote/type47.71 ms、model-beforeinput23.95 ms、replace visible8.24 ms、fragment visible7.78 ms。DOM-present 已在 ready/type/selectAll/可见整文档车道胜过 legacy chunk-on,但 selection/setup-inclusive、promote、select-then-model-beforeinput 仍红,且nativeSurfaceComplete约 1 秒必须单独报告。
  • 真实浏览器原生输入证明:新增 DOM-present 大文档路线,用真实 click +page.keyboard.type断言模型文本、模型选择、DOM 光标与 native-allowedinputtrace;过程中修复了三个真实问题(大文档 keydown 拦截所有可打印键、空 marks{}拒绝原生输入、selection-only 操作污染 DOM node map),并将 DOM 到模型的修复改为先在显式 pre-native 点插入再设置插入后选择。
  • 分组降至 50 + 后台吞吐 8 组/tick(5000 块中位数):legacy chunk-on ready307.75 ms、middle type31.51 ms、select/type32.33 ms、promote/type32.25 ms、replace114.35 ms、fragment114.17 msv2DefaultOmittedready23.41 ms、middle type8.74 ms、select/type39.73 ms、promote/type38.78 ms、model-beforeinput40.52 ms、select-then-model-beforeinput48.81 ms、replace7.83 ms、fragment7.04 ms。默认产品车道全部通过。
  • 10000 块 pending 占位符合并(coalescing):连续 pending 组合并为一个隐藏占位范围,削减 root 子协调压力;5000 块合并中位数保持默认行绿色(v2DefaultOmittedready19.44 ms、type8.92 ms、select/type31.55 ms)。
  • 被拒实验(已回滚):移除 eager active-group 持久化使 10000 块v2DomPresentpromote/type 退化为107.39 ms,回滚;25块分组使表面噪声化(v2DefaultOmittedselect/type199.69 ms),回滚;立即后台挂载灾难性(v2DefaultOmittedselectAll1450.40 ms、middle type1497.83 ms),回滚。

结论:5000 块发布门禁可关闭——默认 DOM-present 中位数在必需车道击败或匹配 legacy chunk-on,真实浏览器原生输入证明存在,interactiveReady/nativeSurfaceComplete分开报告,整文档替换/片段绝不暴露陈旧远 DOM;10000 块立即远选择压力行不能声称修复,已记录为下一个研究/性能 owner(后台优先级、pending 组交互与物化需更深架构而非常量调参)。

11. 反对意见台账与 GPT 评审增量:决策如何被挑战与加固

Slate Maintainer Objection Ledger 用「谁痛 / 强反对 / 钢人反命题 / 取舍 / 为何保留 / 迁移文档证明 / 判定」七列记录了每项变更的争议:

变更谁痛强反对为何保留判定
largeDocument隐式 auto 默认应用作者「我没要求虚拟化」默认是 DOM-present 非 shell,无需用户调参keep
shell 岛显式激进模式大文档用户「你拿走了 40 ms ready 优势」打字与原生语义更重要keep
内部根分组Slate 维护者「这又是 chunking」逃掉 API,偷走机制;无公共getChunkSizekeep
丰富提交脏数据核心维护者「Commit 太大」基准显示当前路径唤醒过多keep
Sidecar 范围索引插件作者「比 decorate 更多 API」评论/只读协作需要 sidecar 所有权keep
选择桥 owner浏览器运行时维护者「这是 ProseMirror 复杂度」shell/group/native 对等需要单一 ownerkeep
命令 capability 元数据扩展作者「Tiptap commands 更熟悉」写生命周期保持tx,可发现性保留keep
Shell 证明门禁产品 owner「拖慢发布」shell 改变浏览器语义keep

GPT Pro 后续评审新增:interactiveReadynativeSurfaceComplete显式区分;默认 DOM-present 分阶段挂载仅作为临时协调债务而非持久虚拟化;陈旧 DOM 规则;根分组生命周期类型与运行时接口(RootGroupState五态、RootGroupRecord优先级、RootGroupRuntime六个方法);六行基准面重命名;trace 字段清单。同时删除:把 shell 冒烟胜利当作默认性能胜利、把 stagedinteractiveReady与 legacy 完整就绪比较而不单独报告nativeSurfaceComplete、保留v2NoIsland行名。

12. 开放问题与决策变更条件

开放问题:

  • 精确默认分组大小:基准250/500/1000/2000,初始可能赢家5001000(后续执行已把 DOM-present 默认组降到50以关闭 5000 门禁);
  • 精确默认走廊:基准+/-1/+/-2/+/-5,初始可能赢家+/-2
  • content-visibility默认:仅在浏览器 find/a11y/mobile 证明按浏览器族通过后启用;
  • Shell auto 升级阈值:shell 证明通过前无默认阈值;
  • 是否把dom-present暴露为公共模式或仅调试模式:在 docs/API 人体工学 pass 后决定。

决策变更条件:

  • 若 DOM-present 分组在修复提交脏数据后仍不能在 5000 块匹配 legacy chunk-on 打字,运行时必须考虑更深层的 Lexical 式 body reconciler 或真正的 EditContext 式输入层;
  • 若 shell 意外通过原生 find/屏幕阅读器/原生选择/IME/mobile 证明且打字变得有竞争力,它可能成为高阈值 auto 升级,但仍不应是首选默认。

13. 结论与仓库内延伸阅读

这份 ralplan 的最终姿态是诚实且可验收的:shell 是可信的显式逃生舱而非默认;默认 DOM-present 在 5000 块发布门禁上击败或匹配 legacy chunk-on 的全部必需车道;interactiveReadynativeSurfaceComplete分离度量,约 1 秒的原生表面预热不被隐藏;真实浏览器原生输入证明独立存在;10000 块立即远选择压力被明确记录为下一个 owner,而非虚假的胜利声明。「Slate v2 应该靠精确知道改了什么来赢,而不是靠把文档藏起来」——这句架构北极星贯穿了脏提交、运行时 id、根分组、sidecar 索引与选择桥的全部设计。

希望继续深入当前仓库的读者可以查阅:

  • 计划本体:2026-05-01-slate-v2-universal-large-document-performance-ralplan.md(含完整 Pass-State Ledger)
  • legacy chunking 剖析:chunking-review.md
  • 架构演进主路线:master-roadmap.md
  • 发布级架构声明与替换门禁:absolute-architecture-release-claim.md、replacement-gates-scoreboard.md
  • 侧车标注/装饰体系:decorations-annotations-cluster.md、decoration-roadmap.md
  • v2 核心实现起点:packages/slate/src/create-editor.ts(editor.read/editor.update/editor.subscribe/editor.extend的承载者)

【免费下载链接】plateRich-text editor with AI and shadcn/ui项目地址: https://gitcode.com/GitHub_Trending/pl/plate

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询