富文本编辑器的性能问题,和普通页面完全不是一个量级。
它不是单次渲染,而是持续交互系统:
- 输入字符要立即响应
- 选区变化要实时同步
- 工具栏状态要跟着文档变化
- 撤销重做要可恢复
- 协作场景还要处理远端更新
这意味着编辑器性能不是“渲染快一点”就够,而是要让整条编辑链路都足够稳定。
先把性能问题拆成四条链路
富文本编辑器的性能瓶颈,通常集中在四条链路:
- 输入链路:按键、组合输入、粘贴、删除
- 渲染链路:节点更新、样式计算、块级重绘
- 状态链路:选区、工具栏、历史记录、插件事件
- 协作链路:远端变更、冲突合并、光标同步
只有先分清问题在哪条链路,优化才不会变成到处加 debounce。
输入性能的关键是减少全量重算
很多编辑器输入卡顿,不是浏览器太慢,而是每次输入都触发了过多工作:
- 重新解析整份文档
- 重新计算整个工具栏状态
- 重新生成所有装饰节点
- 重新同步全局 store
编辑器最需要的是局部更新思路:
- 只更新受影响的 block 或 node
- 选区相关逻辑按最小范围重算
- 工具栏状态延迟到必要时刷新
编辑器只要把“单字符输入”做成全量计算,后面任何优化都会越来越难。
历史记录系统决定了内存与交互成本
撤销重做很容易被当作功能补充,但在编辑器里,它其实是性能问题的重要来源。
如果历史系统每次都保存完整快照,就会出现:
- 文档越长越卡
- 连续输入导致内存飙升
- 撤销一次也要大量 diff
更合理的思路是按操作组织历史:
- 连续输入合并为一个事务
- 样式调整和结构调整分别建模
- 远端协作操作单独记录
这样既能保住撤销体验,也能避免历史栈变成性能黑洞。
插件系统要避免“每个插件都监听一切”
编辑器一旦开放插件能力,最容易出现的架构问题是:所有插件都监听输入、选区和渲染事件。
结果就是:
- 一个输入动作触发十几个插件计算
- 插件之间互相放大性能波动
- 很难定位是谁拖慢了编辑器
更稳的方式是:
- 只暴露必要钩子
- 插件按能力域订阅,而不是全量监听
- 为重插件提供异步计算或延后执行通道
协作能力最容易把编辑器拖慢
多人协作带来的问题不是只有冲突,还有性能放大:
- 远端更新可能频繁打断本地输入
- 光标与评论标记会增加额外装饰层
- 合并逻辑会放大选区和渲染开销
协作场景下更重要的,不是“所有更新实时同步到最细粒度”,而是合理做批处理、节流和局部重绘。
一个常见失败案例:编辑器 demo 很顺,真实文档一长就彻底失速
这种情况通常出现在前期只验证了小文档场景:
- 文档只有几百字
- 插件很少
- 没有表格、评论、图片、嵌入块
一旦进入真实生产环境,问题就集中爆发:
- 输入延迟明显
- 选区抖动
- 粘贴大段内容卡死
- 历史记录与协作互相干扰
根因通常是系统设计从一开始就没有把“长文档、复杂节点、多人协作”当作目标场景。
一份可直接复用的检查清单
- 是否把输入、渲染、状态、协作四条性能链路分开分析
- 单字符输入是否仍然会触发全量重算
- 历史记录是否基于事务与操作,而不是全量快照
- 插件系统是否限制了监听范围与执行时机
- 长文档和协作场景是否被纳入基准测试
总结
富文本编辑器性能优化的本质,是让输入、状态、插件和协作之间的成本始终可控。只要先控制住更新粒度、历史模型和插件边界,编辑器就不会在真实业务中越用越慢。
进一步阅读:


