功能价格文档博客
获取 mote
← 博客
工程·2026年7月5日·阅读需 3 分钟

文档再长,输入也不会变慢的理由

写短笔记时,任何编辑器都很快。差别会在文档变长时显现,而这种差别取决于按下一个键的成本是随编辑范围增长,还是随文档大小增长。

m
mote
在 100 KB 文档中输入 300 个按键后测得的两条数据。分别测量核心完成编辑、重新解析和装饰的时刻(p95 0.44 ms),以及结果绘制到屏幕上的时刻(p95 3.32 ms)。
在 100 KB 文档中输入 300 个按键后测得的两条数据。分别测量核心完成编辑、重新解析和装饰的时刻(p95 0.44 ms),以及结果绘制到屏幕上的时刻(p95 3.32 ms)。

短笔记用任何编辑器写都很快。差别会在文档变长时显现。

如果每次按键都重新读取整篇文档并重新布局画面,那么文档越长,输入也会越慢。这就是在十行笔记中表现良好的编辑器,到了两千行的设计文档中却让人感到卡顿的原因。

mote 没有把这个问题视为以后再解决的性能问题,而是从一开始就将其定为必须遵守的设计原则。

编辑时,只重新读取和布局发生变化的范围。只有打开文件时才重新读取整篇文档。

只重新读取发生变化的地方

在 Markdown 中,前面的行可能会改变后面行的含义。如果在文档上方开启代码块,下面的所有内容都可能变成代码;同一句话位于列表或引用块中时,也会被以不同方式解读。

因此,mote 会记住前文影响确定结束的“安全点”。发生编辑后,它会从附近的安全点开始读取,并在内容重新稳定的瞬间停止。

后面的块不会重新读取。如果行数增加或减少,就只移动它们的位置。这样,成本随实际发生变化的范围增长,而不是随整篇文档增长。

解析器遵循 CommonMark 的逐行算法,并通过了全部 652 项规范测试。但增量处理很容易悄无声息地出错。因此,我们会对随机文档应用 3,600 次随机编辑,并在每次编辑后,将结果与重新读取整篇文档得到的结果进行比较。

这项检查也发现了真实的错误。那是安全点与被编辑的行恰好重合的情况。

用检查来守住性能原则

目标是从按下按键到核心处理结束,p95 不超过 4ms。一旦超过这个时间,就不会将其视为改进事项,而是作为错误处理。

我们也没有只把规则写在文档里。自动检查会确认完整重新解析是否只在打开文件时使用。它还会阻止空闲状态下反复运行的定时器或帧任务。

性能也不由人凭肉眼判断。测试工具会在真实画面中输入按键,并测量处理时间的分布。我们关注的不是平均值或中位数,而是 p95。因为让用户感到卡顿的,并不是平时的速度,而是偶尔出现的长时间延迟。

测量始终使用同一份固定文档。

以实时模式打开测试工具所用固定文档 doc100k.md 的画面。状态栏显示 96,513 个字符和 103 分钟的阅读时间。
以实时模式打开测试工具所用固定文档 doc100k.md 的画面。状态栏显示 96,513 个字符和 103 分钟的阅读时间。

画面也只更新必要的部分

只将解析器改成增量处理还不够。

代码块的语法高亮在每次编辑时都会重新处理所有块。这是最后一项无论编辑范围多大,都会随着文档变长而变慢的工作。

现在,未修改块的语法高亮会直接复用。编辑位置之后的块,只会根据行数的变化移动位置。

屏幕之外的块不会进行布局。因为只有同时缩小重新读取和重新绘制的范围,才能在长文档中维持速度。

也有例外

有时必须重新处理整篇文档。

比如打开文件或换行格式发生变化时。修改链接引用定义后,文档中任何位置的链接都可能发生变化,因此画面更新范围也会扩大到整篇文档。

即使核心很快,画面仍然需要时间等待下一帧。对于 60Hz 的屏幕,帧间隔本身就会成为极限。因此,我们会分别测量核心处理时间和内容实际出现在屏幕上所需的时间,不会把这两个数字放在同一条轴线上。这就是封面上的两条数据不同的原因。

快速的编辑器,并不是把所有事情都做得很快的编辑器。

而是不做那些没有必要做的事情的编辑器。

← 较早不直接绘制文字,而是交给平台处理的理由较新 →为了在其他工具中也显示为相同段落,我们定义了 Enter 键的规则
通过RSS持续关注。
只留下文字
简体中文
© 2026 mote