大文档编辑
大文档编辑的核心目标不是一次性处理全部内容,而是让用户当前看到和正在编辑的区域保持稳定、快速、可预测。DirectSurface UI 中的大文本编辑器和病历编辑器都应遵循这个原则。
性能目标
大文档场景下,交互复杂度应接近:
可视区域节点 + 当前编辑边界复杂度
不应在鼠标移动、光标闪烁、拖选或单字符输入时扫描全部文档、全部页或全部 render tree。
三条主线
大文档优化通常分三条线:
- 虚拟绘制:只绘制可视区域和少量缓冲区。
- 增量排版:只重排变化节点附近内容,能 patch 就不全量分页。
- 输入隔离:textarea 只作为输入承接,不承载整份文档内容。
这三条线要同时成立。只优化绘制但每次输入仍全量排版,用户仍会感觉卡顿。
输入策略
隐藏输入控件应只承接当前输入事件。粘贴几十 MB 文本时,应在 paste 事件中读取剪贴板数据并阻止浏览器把大文本落入 textarea。中文输入结束后也应尽快清空承接控件,避免浏览器对超大 value 做布局和选择计算。
对大文本编辑器来说,光标命中、选区更新和绘制都应该只访问可视行和必要索引。对病历编辑器来说,拖选和绘制应使用全局范围坐标投影到可视页,而不是展开整份文档选区树。
病历编辑器的页面接入、工具栏、批注留痕、打印和释放要求见 病历编辑器接入。
排版策略
病历类文档需要分页。输入字符时可以先尝试快速 patch:
- 当前段落或当前局部结构重新测量。
- 如果高度和分页边界不变,替换当前 render。
- 如果高度变化,再从变化位置向后修正分页。
- 后续可扩展为分批异步排版,但当前可视区域必须优先稳定。
快速 patch 的判断不应只限制纯文本变更。最终判断标准是当前变化能否在局部 render 层面被稳定替换。
绘制策略
绘制只处理可见内容:
- 大文本编辑器只绘制可视行。
- 病历编辑器只绘制可视页和缓冲页。
- 留痕块和批注块只布局可见相关项。
- 光标闪烁只触发必要绘制,不能触发布局。
如果性能浮层显示持续 layout,应优先排查组件在 performLayout 中重复设置子组件状态,例如每次 layout 都创建新数组或调用会标记布局的方法。
内存策略
大文档关闭后,控制器、页面上下文、图片缓存、弹层、输入承接控件和事件订阅都要释放。缓存应按归属放在文档上下文或页面上下文,不要无理由放入全局。
内存验证建议:
- 打开大文档。
- 执行输入、滚动、弹层、批注等操作。
- 关闭页面或销毁应用实例。
- 生成 heap snapshot。
- 查找是否仍有页面、控制器、大数组或 canvas 被引用。
大文档优化必须同时看耗时和内存。只有绘制快但关闭后不释放,长期使用仍然会失控。