架构总览与包分层
1. 先看结论(可先说)
Slate 的核心是一个operation 驱动的、schema-less 的编辑引擎。渲染层(React/DOM)和历史层(undo/redo)是插件叠加,而不是写死在 core。
一句话分层:
slate:数据模型、操作系统、变换与规范化(纯编辑核心)slate-dom:DOM <-> Slate 的映射与浏览器兼容层slate-react:React 组件、事件管线、渲染优化slate-history:基于 operation 的历史批次管理
2. Monorepo 包职责
packages/slate
核心编辑器对象与算法层,入口由 createEditor() 组装:
editor.children / selection / operations / marks- 可覆写行为:
isInline、isVoid、normalizeNode、insertText等 - 核心执行器:
editor.apply(op)
packages/slate-dom
把抽象位置(Path/Point/Range)映射到真实 DOM:
toDOMPoint / toDOMRangetoSlatePoint / toSlateRangefindPath、toSlateNode等
packages/slate-react
React 集成层:
<Slate />上下文<Editable />输入事件编排(selectionchange/beforeinput/composition)- selector 订阅机制 + chunk 渲染优化
packages/slate-history
历史栈插件 withHistory:
undos / redos两个 batch 栈Operation.inverse做可逆回放- merge/saving/split 策略控制批次边界
3. 关键执行链路(从用户输入到文档更新)
- 浏览器触发输入事件(
beforeinput、composition、selectionchange) slate-react把事件转换为 Slate 命令(Editor.*/Transforms.*)- 命令内部发出一个或多个 operation(如
insert_text、set_selection) editor.apply(op)执行统一管线(更新 refs、更新 dirtyPaths、应用变换、normalize、onChange)- React 层在下一个 flush 中拿到
editor.operations,做 UI 同步/订阅分发
这条链路是最重要的“系统观”。
4. 源码证据(建议背)
4.1 createEditor 是可覆写能力的装配点
文件:packages/slate/src/create-editor.ts
- 默认
isInline: () => false/isVoid: () => false - 表示 core 不绑定业务 schema,由业务 plugin 覆写
4.2 apply 是唯一可信执行入口
文件:packages/slate/src/core/apply.ts
- 先更新
PathRef/PointRef/RangeRef - 再
Transforms.transform(editor, op) - 再
Editor.normalize(editor, { operation: op }) - 最后异步 flush
editor.onChange({ operation: op })
4.3 React 层只消费 editor,不替代 core
文件:packages/slate-react/src/components/slate.tsx
<Slate />只把onChange与 editor 绑定editor.children = initialValue初始化一次
5. 为什么要这样拆(设计动机)
- 可扩展性:复杂编辑器(表格、mention、嵌套 block)需要高度可定制,不适合硬编码 schema。
- 可维护性:输入法、浏览器差异属于 UI/DOM 问题,不应污染 core。
- 可组合性:历史、协作、序列化都可建立在 operation 之上。
- 可测试性:core 可在无 DOM 环境下做大量 fixture 测试,React 层做集成行为测试。
6. 口头表述模板
Slate 的架构是“core + adapters + plugins”。
slate只处理文档与 operation,不关心浏览器细节;slate-dom负责点位映射与兼容;slate-react做事件编排与渲染;slate-history在 operation 上实现 undo/redo。
这种拆分的结果是:核心稳定、UI 兼容性问题局部化、性能优化可以在 React 层独立演进。