性能优化:Selector 与 Chunking
1. Slate 的性能瓶颈通常在哪
- 每次输入触发大范围 React 重渲染
- 大文档结构更新导致节点树遍历和 key 映射成本高
- 选区变化导致高频状态刷新
Slate 的策略是两层优化:
- 订阅粒度优化(selector)
- 渲染树分块(chunking)
2. useSlateSelector:避免“每次按键全组件刷新”
文件:packages/slate-react/src/hooks/use-slate-selector.tsx
关键点:
- redux 风格 selector + equalityFn
- 只在 selector 结果变化时更新组件
- 支持
deferred:延迟到Editable渲染后再执行 selector
deferred 的设计非常实用:避免 selector 内部 findPath 读到旧路径。
源码注释(节选):
ts
/**
* If true, defer calling the selector function until after `Editable` has
* finished rendering. This ensures that `ReactEditor.findPath` won't return
* an outdated path if called inside the selector.
*/测试依据:
packages/slate-react/test/use-slate-selector.spec.tsx
3. Chunking:把大 children 列表拆成块
主要文件:
components/chunk-tree.tsxchunking/get-chunk-tree-for-node.tschunking/reconcile-children.tschunking/types.ts
核心思路:
- 对大 children 构建 chunk tree,而不是平铺渲染
React.memo+modifiedChunks精准控制重渲染块movedNodeKeys特判move_node,避免把 move 误判成大量 insert/remove
这是 Slate 近年比较“工程化”的性能演进点。
4. withReact 如何和 chunking 联动
文件:packages/slate-react/src/plugin/with-react.ts
- 默认
getChunkSize = () => null - 当业务覆写
getChunkSize后启用 chunking - 在
move_node时,把被移动节点 key 标记进chunkTree.movedNodeKeys
这一步是 reconcile 正确识别“移动”而非“删除+插入”的关键。
5. 测试覆盖强度
packages/slate-react/test/chunking.spec.ts 的价值很高:
- 初始 chunk 形态
- 插入/删除/移动/更新后的树形对齐
- 大量随机操作一致性测试(随机种子可复现)
这类测试说明 chunking 不是 UI 小优化,而是核心渲染基础设施。
6. 使用建议(业务侧)
- 普通文档先用 selector 优化,不要过早 chunking
- 超长文档(例如数百 block)再评估启用
getChunkSize - selector 返回对象时务必配 equalityFn,避免伪变化
- 避免在高频路径里调用昂贵
Editor.nodes全量扫描
7. 回答模板
Slate 的性能不是靠单点技巧,而是“订阅粒度 + 渲染结构”双优化。
useSlateSelector解决不必要订阅更新,chunking 解决长列表渲染成本。
尤其move_node场景通过movedNodeKeys纠正 reconcile 语义,这是一个典型的工程级性能设计。