Slate v2 大文档 Shell 渲染证明:岛屿化渲染层如何把万级块文档的编辑成本真正降下来
2026/9/17 6:12:21 网站建设 项目流程

Slate v2 大文档 Shell 渲染证明:岛屿化渲染层如何把万级块文档的编辑成本真正降下来

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

本篇文章基于 plate 仓库内docs/plans/2026-04-11-slate-v2-large-document-shell-proof-batch.md(Slate v2 大文档 Shell 证明批次)的完整记录展开,结合同仓库中 Proof-First 大文档层计划、DOM 协调修复方案、rerender 广度车道方案等配套文档,还原这一批次的工程脉络:它证明了"远端岛屿只渲染廉价 Shell、不再挂载可编辑子树"这条路线能够真实削减运行时开销,并定位了后续激活路径与模型级全选/粘贴两条主攻方向。读完本文,你将理解 Slate v2 大文档层的岛屿分类模型、结构化 DOM 协调在部分挂载下的正确性约束、三条基准车道的用法与判读方式,以及该批次的边界(哪些问题明确未解决)。

背景:为什么需要 Shell 证明批次

Slate v2 在 1 万块(10000-block)级别的文档上一直面临可编辑渲染成本问题。此前提出的"语义岛屿(semantic islands)"第一版原型被否决,原因是它并不诚实:远端岛屿仍然渲染了完整的后代树,Ctrl+A、粘贴等宽操作(broad ops)仍会回退到昂贵的整树路径,层架构付出了复杂度却没有真正移除运行时工作。这一教训被记录在 Proof-First 大文档层计划 中:"grouped wrappers plus CSS hints are not enough"(分组包装加 CSS 提示不够)

于是有了三条非协商规则(non-negotiable rules):

  1. 分块(chunking)不作为基础架构;
  2. 局部订阅(local subscriptions)仍作为基础层;
  3. 活动走廊(active corridor)仍使用实时 DOM 真相;
  4. Pretext 仍只是规划层,不是活动编辑真相;
  5. DOM 保持足够的在场性以满足浏览器行为,不直接跳到虚拟化;
  6. 不再交付另一个仅包装器的原型。

Shell 证明批次就是在这个前提下,第一次以"诚实"的方式验证 Shell 路线:远端岛屿停止挂载可编辑后代。这正是本批次标题中 "shell-only proof" 的含义——只证明 Shell 本身成立,不与宽操作集成。

岛屿模型与 Shell 设计

岛屿种类(Island Kinds)

Proof-First 计划把第一版刻意收窄为四类顶层结构:

  • 顶层段落/块组(top-level paragraph/block group);
  • 表格子树(table subtree);
  • 空元素/嵌入块(void/embed block);
  • 列表子树(list subtree)。

暂不从标题推断章节(Do not infer sections from headings yet)。

Shell 必须做什么、禁止做什么

Shell 模型的核心纠正是:活动/邻近岛屿渲染真实的可编辑后代树,远端岛屿渲染廉价 Shell,而非可编辑后代

Shell 仍须暴露有用的 DOM:

  • 可见文本内容,支撑滚动与粗略阅读;
  • 稳定的 Shell 根属性,供激活(activation)使用;
  • 内在高度提示(intrinsic height hints)。

Shell 严禁挂载:

  • EditableDescendantNode
  • EditableText
  • 逐叶子订阅(per-leaf subscriptions);
  • 昂贵的覆盖层投影工作。

即"渲染更少、只变更实际挂载的内容、精确保留远端子树文本",这是真正的收益所在,其余都是表象。

本批次保留的工作(Kept Work)

批次文档记录,本批次新增了内部大文档规划器与 Shell 渲染器,并接入了现有渲染入口:

  • 新增large-document内部模块:create-island-plan.ts(岛屿规划器)、classify-island-kind.ts(岛屿种类分类器)、island-shell.tsx(Shell 渲染器);
  • 通过editable-text-blocks.tsxEditableBlocks接入可选的 Shell 化(opt-in shelling);
  • huge-document.tsx演示中增加查询参数控制(query-param control),用于 A/B 运行;
  • 新增专用证明车道replacement/huge-document-islands.mjs基准;
  • 修复既有广度车道slate-react-rerender-breadth-benchmark.tsx的 JSX-runtime 假设,使其恢复为被保留的车道。

需要说明的是:上述文件路径来自文档所记录的外部源码仓库(slate-v2 / plate-2)。在当前 plate 仓库中,与之对应的可查阅物证包括:大文档演示的实现 huge-document-demo.tsx、官方示例说明 huge-document.mdx,以及基准车道的注册信息 slate-v2.json(其中登记了react-rerender-breadthreact-huge-document-full等车道及其运行命令)。

关键修复:结构化 DOM 协调必须变为子集感知

这是本批次中"如果不修复就会静默损坏文档文本"的关键点,单独成篇记录在 Shell-only 大文档岛屿必须在 DOM 协调期间保留远端子树。

问题根源

EditableBlocks通过读取[data-slate-node="text"]节点并按快照顺序重建文本内容来从 DOM 协调。这只有在整棵可编辑树都已挂载时才成立。一旦远端岛屿变成廉价 Shell,这个假设就失效了:

  • 远端岛屿有意停止渲染EditableText
  • DOM 协调仍然只扫描已挂载的文本节点;
  • 在一个活动岛屿中输入时,协调游标会提前耗尽已挂载文本列表,然后用空字符串''填满远端 Shell 文本节点——导致一个活动岛屿的输入把远端文本全部清空。

修复方案

让 DOM 协调只作用于已挂载的顶层岛屿:

  • 活动岛屿仍从 DOM 文本节点协调;
  • Shell 化的顶层岛屿直接从已提交快照(committed snapshot)拷贝;
  • 协调游标只为已挂载的顶层运行时 id 消费 DOM 文本。

这样 Shell 模式保持诚实:渲染更少、只变更实际挂载的内容、远端子树文本被精确保留。方案文档给出的预防性结论同样值得记录:任何 Shell 或遮挡层在相信性能收益之前,都必须审计 DOM 协调假设;"整棵树已挂载"这一假设在 Shell 模式下必然错误;基准收益本身不够,还要检查快照模型在部分挂载下是否仍然正确。

三条基准车道与判读

车道 1:默认运行时(1000 块)

pnpm bench:replacement:huge-document:local在 1000 块下的记录结果:

指标Shell 批次(v2)Legacy
ready527.95ms720.87ms
type12.48ms19.85ms
select-all3.59ms75ms
paste34.85ms107.75ms

注意 select-all 从 75ms 降到 3.59ms、paste 从 107.75ms 降到 34.85ms,这组数据说明在 Shell 层未启用或处于默认状态时,运行时本身并未退化。

车道 2:rerender 广度车道

pnpm bench:react:rerender-breadth:local记录读取结果:

  • #5131:宽 hook(broad hook)按契约仍然宽(useSlate()本就是整编辑器级响应式);
  • #3656:兄弟叶子重渲染数 0;
  • #4141:祖先节点重渲染数 0。

广度车道方案在 Slate React rerender 广度大多已局部化,useSlate 是仅剩的宽 hook 中有完整记录:它把两个长期被混为一谈的故事分开——"宽 hook 按设计是宽的"与"后代失效已经局部化"。批量记录中该车道还包含更细的读数(20 次选择变更产生 20 次useSlate()useSlateSelection()重渲染、无关顶层块切片重渲染 0;编辑叶子重渲染 1、兄弟叶子 0、父块 0;深层编辑叶子 1、重渲染祖先 0、兄弟分支 0)。

车道 3:Shell 证明车道(10000 块)

pnpm bench:replacement:huge-document:islands:local在 10000 块下的记录结果:

  • ready:527.86ms
  • top typing(顶部输入):38.85ms
  • shell promotion(Shell 提升):54.10ms
  • shell count(Shell 数量):99

与上一次干净的未启用 Shell 的 10000 块基线相比(ready 1229.49ms、top typing 97.47ms),本次 Shell 证明削减了:

  • ready 约 701.63ms;
  • top typing 约 58.62ms。

批次文档的结论是:"That is real enough to keep."——这个收益真实到值得保留。

Verdict:证明了什么、没证明什么

已证明

  • Shell-only 远端岛屿可以移除真实运行时工作;
  • 收益大到足以支撑继续投入;
  • 局部提升(local promotion)足够廉价,是一条可行的路径。

明确未证明

  • 宽操作(broad ops)尚未解决;
  • Shell 支撑的选择(shell-backed selection)尚未解决;
  • 远端 Shell 粘贴(far-shell paste)尚未解决。

批次文档因此给出的下一步是显而易见的:

  • Proof-First 计划的 Phase 2 与 Phase 3(激活路径、模型级宽操作快路径);
  • 交互时提升到可编辑 DOM(promotion into editable DOM on interaction);
  • 对 Shell 支撑的选区执行模型驱动的全选与粘贴。

后续方向的仓库佐证

该批次之后的相关方案在当前仓库中有连续记录,可作为继续追踪的入口:

  • 激活路径要求"Shell 提升必须把选区移入被提升的岛屿,否则只是表面工作",见 Shell 提升必须把选区移入被提升的岛屿:mousedown 时提升岛屿、在模型中选中该顶层岛屿起点、聚焦真实编辑器根,并以"promote then type"而非只测 promote 来验证;
  • 宽操作快路径要求"Shell 支撑的选区在 DOM 中必须 fail closed,宽操作走模型",见 Shell-backed 大文档选区必须在 DOM 中 fail closed 并通过模型执行宽操作;
  • 输入性能前置条件要求"大文档输入需要先削减 selector fanout 再做岛屿",见 大文档输入需要 selector fanout 削减;
  • 粘贴性能约束要求"大文档粘贴不应重渲染未变更的后代",见 大文档粘贴不应重渲染未变更的后代;
  • 执行批次可参考 active-corridor-promotion-batch 与 large-document-broad-ops-batch。

小结

Shell 证明批次的意义不在于交出一套完整的大文档层,而在于用可复现的基准车道证明了方向本身:远端岛屿渲染 Shell、活动走廊保持实时 DOM 真相、提升局部化,是比"分组包装 + CSS 提示"和分块叙事更值得继续投入的大文档策略。它同时把正确性问题(部分挂载下的 DOM 协调)与性能问题(宽操作、Shell 支撑选区、远端粘贴)明确分开,为后续 Phase 2/3 的推进划出了清晰的边界与验收标准。

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

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

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

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

立即咨询