插件系统内核:创建、解析、执行
1. 心智模型
Plate 插件不是“配置对象”,而是 可组合、可扩展、可重写的行为单元。一个插件最小需要 key,其余能力按需叠加:
options/selectors:状态与查询api/transforms:能力暴露与状态变更handlers/render/rules:事件与渲染行为dependencies/priority:执行顺序控制
对应定义可在 packages/core/src/lib/plugin/BasePlugin.ts 看到。
2. 插件创建:从声明到可扩展实例
核心工厂是 createSlatePlugin,Plate React 场景再包一层 createPlatePlugin:
createSlatePlugin:packages/core/src/lib/plugin/createSlatePlugin.tscreatePlatePlugin:packages/core/src/react/plugin/createPlatePlugin.ts
为什么这两个层级很关键?
createSlatePlugin负责基础插件能力(无 React 绑定)createPlatePlugin通过toPlatePlugin增加 React 侧能力
这让“核心逻辑”和“框架集成”天然解耦,便于 SSR/静态环境和 React 场景共存。
3. 解析阶段:resolvePlugins 做了什么
插件真正生效是在解析阶段,入口是:
packages/core/src/internal/plugin/resolvePlugins.ts
该阶段做了 6 件事:
- 初始化 editor 缓存与 plugin registry
- flatten + resolve 嵌套插件
- 同 key 合并(后来的覆盖前面的)
- 先按
priority排序,再按dependencies重排 - 计算快捷缓存(handlers/render/nodeProps/shortcuts)
- 创建每个插件的 options store(zustand-x)
这不是“简单数组遍历”,而是一个可复用的插件编译过程。
4. 顺序控制:priority + dependencies
源码里先按优先级降序,再 DFS 访问依赖(缺失依赖会 debug.warn),这能同时满足:
- 全局优先级可控
- 局部依赖可声明
- 错误依赖可观测
这也是为什么可以说:Plate 的插件顺序不是“约定”,是“显式图结构 + 优先级”的混合模型。
5. 覆盖机制:可定制但可追踪
resolvePluginOverrides 支持 3 类覆盖:
- 启用/禁用覆盖(
override.enabled) - 组件覆盖(按优先级决定生效)
- 插件配置覆盖(
override.plugins)
这一套设计让业务方可以局部重写核心行为,同时不需要 fork 全插件。
6. 历史证据:动态插件与基类插件重构
- PR #1882 显示动态插件问题会波及大面积包(当时 changeset 涉及 61 个包),说明插件编排是系统级问题,不是单点 bug。
- PR #3526 “Base plugin” 超大规模重构(977 files),本质是把“基础插件能力”和“React 变体”边界进一步固化。
7. 回答模板
你可以这样回答“Plate 插件系统怎么工作的”:
- 创建:
createSlatePlugin/createPlatePlugin产出可扩展插件实例 - 解析:
resolvePluginsflatten/merge/sort/dependency-order/store - 执行:通过缓存管线把 handlers、rules、render、shortcuts 路由到 editor 生命周期
- 定制:
override和extend*提供有边界的重写点
小结
Plate 插件系统的价值不只是“可扩展”,而是 可扩展 + 可预测 + 可治理。这三点一起,才能支撑几十个插件包长期演进。