Skip to content
目录

Selection 与 Path/Range 映射机制

1. 这是 Slate 最难、也最有区分度的部分

很多编辑器问题最后都落在“选区映射”:

  • 为什么会报 Cannot resolve a Slate point from DOM point
  • 为什么光标跳动
  • 为什么 onChange 里 findPath 偶发不准

根因通常是:DOM 与 Slate 树在某个时刻失步


2. 映射核心在 slate-dom,不是 slate-react

核心 API(packages/slate-dom/src/plugin/dom-editor.ts):

  • toDOMPoint / toDOMRange
  • toSlatePoint / toSlateRange
  • findPath

slate-reactReactEditor 只是复用 DOMEditor:

  • 文件:packages/slate-react/src/plugin/react-editor.ts
  • 结论:React 层主要做事件编排,不重写映射算法主体

3. 双向同步路径(必须会说)

DOM -> Slate

Editable 里监听 selectionchangebeforeinput,调用:

  • ReactEditor.toSlateRange(...)
  • Transforms.select(editor, range)

Slate -> DOM

在 layout effect 中,拿 editor.selection 转为:

  • ReactEditor.toDOMRange(...)
  • domSelection.setBaseAndExtent(...)

这就形成闭环:DOM 事件驱动状态更新,状态再驱动 DOM 选区校准


4. 关键兼容细节(进阶加分)

dom-editor.ts / editable.tsx 中有大量兼容分支:

  • zero-width 节点 offset 对齐(Slate offset=0,DOM 常是 1)
  • void 节点 offset 修正
  • ShadowRoot 选择问题
  • Firefox 多 range 与 table 选择兼容
  • Chrome/Safari 对 beforeinput 与 composition 的顺序差异

结论:富文本编辑器不是“业务复杂”,而是“浏览器行为复杂”。


5. findPath 的时序陷阱

Issue #5938 很典型:

  • NODE_TO_INDEX / NODE_TO_PARENT 在渲染后更新
  • onChange 触发可能早于视图同步
  • 在特定时机 DOMEditor.findPath 可能拿到旧映射

对应设计演进:

  • PR #5800 尝试把 path 计算脱离 DOM 依赖

这类问题本质是“映射索引更新时序”的工程权衡。


6. 回答模板

Slate 选区系统是双向映射:DOM 选区变化经 toSlateRange 回写 editor,editor.selection 再经 toDOMRange 推回原生 Selection。
映射层在 slate-dom,并带大量浏览器兼容逻辑(zero-width、void、shadow root、Firefox table range)。
线上常见选区 bug 通常是 DOM 映射缓存与 React 渲染时序错位,不是简单 API 调用错误。

MIT License.