文字排版的未来不在 CSS
🐰 Drag the bunny to see real-time text reflow powered by @chenglou/pretext
网页通过一条三十年前为静态文档设计的流水线来渲染文字。浏览器加载字体,将文本塑造成字形,测量它们的总宽度,确定换行位置,然后垂直排列每一行。每一步都依赖前一步的结果。每一步都要求渲染引擎查阅它的内部布局树——这个结构的维护成本极高,以至于浏览器将访问权隐藏在同步回流屏障之后,而这些屏障可以一次性冻结主线程数十毫秒。对于一篇博客文章中的一段话来说,这条流水线是透明的。浏览器在读者的目光从地址栏移到第一个字之前,就已经完成了加载、布局和绘制。但网页已不再是一系列静态文档的集合。它是一个应用平台,而这些应用需要以原始流水线从未预料到的方式来了解文本。
一个消息应用需要在渲染虚拟列表之前知道每个消息气泡的精确高度。一个瀑布流布局需要知道每张卡片的高度以避免重叠。一个编辑页面需要让文字围绕图片、广告和交互元素流动。一个响应式仪表盘需要在用户拖动面板分割线时实时调整和重排文字。这些操作中的每一个都需要文本测量。而当今网页上的每次文本测量都需要一次同步的布局回流。其代价是毁灭性的。测量一个文本块的高度会迫使浏览器重新计算页面上每个元素的位置。当你连续测量五百个文本块时,就会触发五百次完整的布局计算。这种模式被称为布局抖动,它是现代网页上卡顿的最大来源。
Chrome 开发工具会用愤怒的红色条标记它。Lighthouse 会扣除你的性能分数。但开发者没有替代方案。CSS 没有提供在不渲染文本的情况下计算文本高度的 API。信息被锁在 DOM 背后,而 DOM 让你为每一个答案付出代价。开发者们发明了越来越绝望的变通方案。估算高度用猜测代替了真实测量,导致内容在猜测错误时明显跳动。ResizeObserver 监视元素的尺寸变化,但它异步触发,总是至少晚了一帧。IntersectionObserver 追踪可见性,但对尺寸一无所知。content-visibility 允许浏览器跳过渲染屏幕外的元素,但它会破坏滚动位置和无障碍访问。虚拟滚动器估算高度以减少 DOM 节点,但它们在可变高度内容面前举步维艰,因为测量高度恰恰需要它们试图避免的回流。
性能提升不是递增的,而是质变的。零点零五毫秒对三十毫秒。零次回流对五百次。网页上的文本布局问题不是 CSS 问题,而是架构问题。CSS 的设计初衷是在浏览器完成布局之后描述文档的外观。它从来不是为了在布局发生之前回答关于文本的问题。每一次从 CSS 中提取测量信息的尝试都会迫使浏览器走完它的整条布局流水线。答案是将文本测量完全移出浏览器。在 JavaScript 中执行塑形、测量和换行——使用浏览器使用的相同字体度量——但不触碰 DOM。将结果以纯数字的形式返回:行数、总高度、字符位置。让开发者随心所欲地使用这些数字:调整容器大小、定位元素、驱动动画、虚拟化列表。
这就是文本布局引擎所做的事情。它接受一个字符串、一个字体和一个最大宽度,返回每一行断行的精确位置和结果块的精确高度。它不创建任何 DOM 元素。它不触发任何回流。它在微秒内完成,而不是毫秒。这些影响像瀑布一样贯穿技术栈的每一层。虚拟滚动器可以在不渲染任何项目的情况下知道每个项目的精确高度。瀑布流布局可以在一次遍历中定位每张卡片。编辑器可以在不强制布局的情况下计算光标位置。聊天应用可以在列表渲染之前为每个气泡确定大小。动画引擎可以以每秒六十帧的速率插值文本布局属性。
文字成为视觉构图中的一等公民——不再是静态的块,而是一种在实时中自适应的流动材料。开发者不再询问浏览器文字最终在哪里。开发者告诉浏览器文字应该在哪里。这颠覆了 JavaScript 与渲染流水线之间的传统关系。开发者不再编写 CSS 规则然后测量结果,而是在 JavaScript 中计算布局然后直接应用。DOM 变成了渲染目标,而非布局引擎。CSS 盒模型变成了简单场景的便利工具,而非复杂场景的唯一选择。这种架构转变解锁了之前在网页平台上不可能或代价过高的能力。
考虑一个需要显示光标的文本编辑器。今天,编辑器必须创建一个隐藏元素,插入文本到光标位置,测量元素,提取坐标,然后清理。这次通过 DOM 的往返需要毫秒级时间,并导致影响页面上每个其他元素的回流。有了文本布局引擎,编辑器直接从字符索引计算光标位置。没有 DOM 元素。没有回流。没有副作用。结果是一对坐标,在微秒内可用。考虑一个需要定位在段落中某个词旁边的提示框。今天,你需要 Range 对象、getBoundingClientRect,以及对换行的仔细处理。有了预计算的文本布局,你已经知道每个字符的精确位置。提示框的放置只是一次简单的查找。
考虑一个根据可用宽度改变列数的响应式设计。今天,你渲染内容、测量它、调整列数、重新渲染、再次测量,然后祈祷它能收敛。有了文本布局引擎,你为每种可能的列宽计算每个文本块的高度,找到最优配置,然后只渲染一次。没有迭代。没有收敛。没有布局抖动。网页平台已经超越了它以文档为中心的起源。处理文字的工具没有跟上步伐。渲染流水线在服务静态内容方面表现出色,但当应用需要以编程方式推理文本时,它就变成了瓶颈。将文本布局从浏览器中移出并放入应用空间,这不是一种变通方案,而是网页平台演化的自然下一步。
网页上文字排版的未来不是更多的 CSS 属性。不是更多隐藏在异步回调背后的浏览器 API。而是快速的、同步的、纯粹的计算——给开发者他们需要的数字,却不附带他们承受不起的代价。它是作为库的文本布局,而非平台特性。它是不需要渲染的测量。它是不需要回流的布局。它是布局抖动的终结。而它今天就已可用。