ENZH

Web 做了三十年都没做到的事,被一个 15KB 的库解决了

📊 幻灯片

上周,Cheng Lou 发了一个库叫 pretext

三天,13000 个 star。

你可能会说,开源项目爆火不稀奇。但这个库做的事情,让我看了之后在椅子上坐了五分钟没动。

它做的事情是:告诉你一段文字渲染出来有多高。不碰 DOM。

一行代码的事

在 iOS 上,你想知道一段文字渲染后多高:

label.sizeThatFits(CGSize(width: 300, height: .greatestFiniteMagnitude))

一行。没有副作用。不会让整个页面重新算一遍布局。

Android 有 StaticLayout。Flutter 有 TextPainter。Jetpack Compose 有 TextMeasurer

每一个主流原生平台,都把「量文字」当成一个独立的、可以随时调用的操作。

Web 呢?

Web 的唯一办法是:把文字塞进 DOM → 强迫浏览器重新计算整棵渲染树的布局 → 读出高度 → 把元素删掉。

这个操作叫 layout reflow。它是浏览器里最贵的操作之一。

更要命的是,CSS 的布局是全局耦合的。你改了一个元素,整页都得重新算。

三十年的技术债

为什么会这样?

因为 CSS 是 1994 年设计的。那时候 Web 是学术论文交换系统。没人想到它会变成应用平台。在纯文档的场景下,「独立查询布局结果」这个需求根本不存在,所以没人设计这个接口。

1994 年完全合理的决定。三十年后,变成了天文数字的技术债。

2012 年 Facebook 用 HTML5 做移动端,崩了。一个核心原因就是 DOM 的全局 reflow 机制扛不住 News Feed 的性能要求。Facebook 花了九个月从头写原生 iOS 客户端。启动时间从十秒降到四秒。

后来他们造了 React Native 的 Yoga 布局引擎——本质上就是在绕过 CSS 缺失的那一层抽象。

W3C 知道这个问题。CSS Houdini 的 Font Metrics API 就是为了解决它。但到 2026 年了,没有一个浏览器实现了。

Pretext 怎么做的

Pretext 找到了一个后门:canvas.measureText()

Canvas 的文字渲染和 DOM 共用同一个字体引擎,但 Canvas 完全在布局树之外运行。通过 Canvas 量文字,永远不会触发 reflow。

两个阶段:

冷路径(一次性): prepare(text, font) —— 用 Intl.Segmenter 做语言感知的分词(处理中日韩文、双向文字、emoji、软连字符),通过 Canvas 测量每个片段的宽度,缓存起来。500 段文字大约 19ms。

热路径(便宜): layout(prepared, maxWidth, lineHeight) —— 纯算术,在缓存的宽度数据上模拟浏览器的换行算法。返回 { height, lineCount }。完全不碰 DOM。500 段文字 0.09ms

准确度?Cheng Lou 把《了不起的盖茨比》全文在 Chrome、Safari、Firefox 上渲染,逐像素和 Pretext 的预测对比,反复迭代直到匹配。测试语料扩展到了泰语、中文、韩语、日语、阿拉伯语。

不只是快

性能是最明显的收益。但更深的影响是 Pretext 解锁了什么:

真正的虚拟列表。 现在的虚拟列表要么猜高度,要么先渲染再测量(导致可见的跳动)。Pretext 让你在元素进入视口之前就知道精确高度。零布局偏移。

多行收缩包裹。 CSS 没有办法说「让这个容器的宽度刚好等于最宽那行的宽度」。聊天气泡、标签、徽章——全都比需要的宽。Pretext 的 walkLineRanges() 可以二分搜索出数学上最紧的宽度。这是一个 Web 字面意义上无法表达的布局原语。

文字绕障碍物排版。 layoutNextLine() 让每一行有不同的最大宽度。文字绕着图片、形状、移动物体排版——杂志式的排版 CSS 因为复杂度放弃了——现在在用户态就能做。

更深的一课

我的朋友 yage 写了一篇非常好的分析,从架构设计的角度看这件事:

好的抽象让你选择在哪一层工作。坏的抽象把所有层粘在一起,不给你选择。

CSS 选择隐藏中间布局状态。1994 年看起来干净优雅。三十年后,利息高得吓人。

yage 画了一个很尖锐的类比:我们现在给 AI 系统设计 API 也在犯同样的错误。面向人类的 API 捕获底层错误,抛出一个干净的「操作失败,请重试」。对人类来说这是保护认知带宽。但当使用者变成一个需要「尝试 → 反馈 → 修正」循环的 AI Agent 时,这种保护性抽象就变成了障碍。

AI 需要观察中间状态。不是让你把它封在黑盒子里。

Pretext 的架构——在现有系统旁边开一个独立的观察窗口,而不是替换它——就是正确的模式。

去看 Demo

如果你还没看过 pretext 的 demo,现在就去。

十万个不同高度的文本项做瀑布流排版,120fps。文字实时绕着移动障碍物排版。聊天气泡完美收缩包裹——每个气泡的宽度精确到像素。

社区 demo 也很疯狂。pretext-explosive 把每个字符变成物理粒子,点击爆炸。illustrated-manuscript 让中世纪手稿的文字绕着动画龙排版。

这些不是技术炫技。它们是以前在 Web 上不可能——或者至少贵得离谱——的 UI 模式。

15KB。零依赖。一个人用 AI 辅助编码在几周内完成。

有时候最重要的基础设施,不是最复杂的。而是补上了最该存在、却一直不存在的那一层。


Pretext:github.com/chenglou/pretext。yage 的架构分析:yage.ai/web-layout-tradeoff.html

AI 杂谈Part 12 of 12
← PrevNext →

© Xingfan Xia 2024 - 2026 · CC BY-NC 4.0