React 绑定与渲染层分离
1. 设计目标
Plate 不把 React 作为唯一运行环境。它区分:
- 无 React 核心层:
withSlate/createSlateEditor - React 增强层:
withPlate/createPlateEditor/usePlateEditor
这让同一插件系统可以在服务端、静态处理、React 客户端里复用。
2. 代码证据
2.1 非 React 入口
packages/core/src/lib/editor/withSlate.tspackages/core/src/lib/editor/createSlateEditor.ts
2.2 React 入口
packages/core/src/react/editor/withPlate.tspackages/core/src/react/editor/usePlateEditor.tspackages/core/src/react/plugins/react/ReactPlugin.ts
withPlate 会在 withSlate 基础上追加 React 核心插件(getPlateCorePlugins),而不是复制一套编辑器系统。
3. 为什么这种分层很重要
- 静态能力可独立演进:序列化、反序列化、规则处理不依赖 React 生命周期
- Hook 污染可控:React 专属逻辑集中在 React 插件层
- 调试边界清晰:出现“Invalid hook call”时能快速定位是不是把 React 插件带进了纯静态链路
4. 真实案例:serializeHtml Invalid Hook Call
Issue #2804 是非常典型的教学素材:
- 现象:
serializeHtml场景触发 React hook 错误 - 社区 workaround:过滤掉依赖 React 运行时的插件(如
blockSelection、toggle) - 启示:静态序列化链路要与 UI/交互插件解耦
这个问题不是“某个 if 写错了”,而是架构边界被混用导致的。
5. 可说的工程经验
- 在编辑器系统里,“渲染层插件”与“数据层插件”必须明确分组
- 不要拿生产编辑器完整插件数组直接喂给导出/静态化流程
- 可以按场景建立插件 profile(edit profile / serialize profile)
6. 与 Base Plugin 重构的关系
PR #3526 的“Base plugin”方向,本质就在强化这种边界:基础行为与 UI 绑定分离,减少跨层耦合。
小结
Plate 的 React 绑定不是“包一层 hook”,而是完整的分层策略。真正理解这点,你就能解释很多看似零散的问题(SSR、序列化、hook 异常、插件复用)。