笨重的根源不是 Markdown,而是 Web 引擎
仅仅打开 Markdown 编辑器风扇就开始转,并不是因为 Markdown 的开销高。在相同条件下对同一篇文档进行测量,就会发现成本来自渲染引擎和与时钟绑定的循环。

我在笔记本电脑上打开了 Markdown 编辑器。什么都没有输入,风扇却开始转了。
一开始,我以为是 Markdown 导致的。我以为是因为不断重新绘制文档才变慢的。
真的是这样吗?
我在相同条件下进行了比较
我将屏幕尺寸、渲染方式和文档全部设为相同。打开一份 7.5KB、113 行的文档,然后在 25 秒内什么都不做。输入时,也以相同的速度输入相同的文字。
使用 Web 引擎绘制预览的 apostrophe,空闲 CPU 使用率为 34.6%,内存占用为 378MB。进程有 5 个。
不使用 Web 引擎、以内联方式绘制同一份文档的原型,CPU 使用率为 0.7%,内存占用为 64MB。进程只有一个。
两者都在绘制 Markdown,但结果相差很大。
在 apostrophe 中仅关闭预览后,CPU 使用率降至 7.0%,内存占用降至 133MB。应用和工具包都没有改变。改变的只有绘制预览的方式。
我还测量了完全不绘制 Markdown 的文本编辑器。CPU 使用率为 2.2%,内存占用为 79MB。
绘制 Markdown 的原型反而更轻量。
这时我才确定。问题并不在 Markdown。
即使什么都不做,它每秒也在工作 60 次
原因出在让预览滚动与正文保持同步的代码上。
这段代码并不是只在滚动时运行。每隔 16ms,定时器就会唤醒,并在 WebKit 进程中执行 JavaScript。收到响应后,又会重新设置定时器。
即使没有人操作窗口,它每秒也会工作大约 60 次。
使用 Web 引擎后,编辑文档的区域和绘制预览的区域会变成两个不同的世界。让两边状态保持一致,最简单的办法就是不断检查。如果不单独测量空闲 CPU,这项开销很难被发现。
输入时,差距变得更大。apostrophe 的 CPU 使用率为 180%,原型则为 41%。
一边每次按键都要经过浏览器进程。另一边只为发生变化的行重新添加标签。
我把测量结果变成了设计原则
这个原型并没有采用什么特别的优化。
样式通过标签应用在文本上。多次编辑会被汇集到一个会自行消失的一次性 idle 中。重新添加标签的范围被限制在发生变化的行。进程也只使用一个。
这并不是增加了什么,而是没有放入不需要的东西。
此后,我制定了两条设计原则。
第一,不把 Web 引擎加入依赖项。
第二,如果文档没有变化,也没有动画,就不安排帧、定时器或 tick。
这些原则很难在后期补上。Web 引擎不只是一个简单的库,它会改变应用的结构。一旦加入,移除它就等同于重新开发。定时器也是如此,如果没有规则,每增加一个功能就会多出一个定时器。
作为代价,我不得不放弃简单的做法。无法通过轮询检查文件变更,也不能把公式交给 WebView 处理。所需的布局必须自己实现。
为了避免这些原则逐渐变得模糊,每次发布时,我都会在真实设备上重新测量。

查看数字时也有需要注意的地方
实验采用软件渲染进行。CPU 使用率会比真实 GPU 环境下更高。因此,与其看绝对值,更应该看比例。
原型空闲时 0.7% 的 CPU 使用率也大多来自光标闪烁。关闭闪烁后,几乎无法将它与 0 区分开来。
这并不意味着所有基于 Web 引擎的应用都会遇到相同的问题。这次的结果仅限于 apostrophe。也有像 Chromium 系列那样会在空闲状态下减少工作的引擎。
原型的功能也不多。它没有表格、公式、导出和查找功能。能够比较的仅限于渲染成本。
即便如此,有一点是明确的。
产生成本的并不是 Markdown,而是渲染引擎和永不停歇的循环。
只要准确找到原因,轻量化就不再是一项新功能,而会变成减法。