Pretext — 绕过 DOM 的文本布局引擎

一个纯 TypeScript 库,在不触碰 DOM 的情况下测量和排版多行文本。48 小时内拿到 13,000+ GitHub stars。 日期:2026-03-28 发布 作者:Cheng Lou(@chenglou) GitHub:https://github.com/chenglou/pretext


核心问题

每个前端开发者都遇到过这个场景:你需要知道一段文本在特定宽度下有多高。浏览器给你的唯一办法是把元素塞进 DOM,然后读 offsetHeight 或调 getBoundingClientRect()

这会触发同步 reflow。一条消息无所谓,几千条消息呢?

聊天应用的虚拟滚动就是个典型的受害者。每个气泡要先量出高度才能定位,而高度只能从 DOM 拿。几千条消息,几千次 reflow,性能就崩了。


Pretext 怎么做

绕开 DOM,用数学算。

核心只有两个函数:

  1. prepare() — 一次性完成文本分段、字素切割、Canvas 测量,返回缓存句柄。500 个文本块大约 19ms。
  2. layout() — 热路径,纯算术。同样 500 个块 0.09ms。

19ms 换一次,之后每次 0.09ms。你可以提前算出任何文本在任何宽度下的高度和行数,完全不需要碰浏览器布局引擎。

还有一个被低估的 API:walkLineRanges()。它做的是”多行 shrinkwrap”。CSS 的 width: fit-content 只能按最宽的折行定宽,最后一行短了就留大片空白。Pretext 用二分搜索找到”恰好 N 行”的最窄宽度,零 DOM 读取。做聊天气泡的人应该懂这意味着什么。


文本分段不是按空格切词

这部分是真正脏活累活:

  • CJK 字符逐字断行
  • 阿里语系标点聚类
  • Emoji 宽度修正(Chrome 和 Firefox 在 macOS 上 Canvas 测量和 DOM 测量不一致)
  • 双向文本(BiDi)
  • 软连字符

早期版本把《了不起的盖茨比》全文跑过多个浏览器验证测量精度。这不是随便调个 API 就能搞定的事。

API 例子

只要高度:

const prepared = prepare('Hello 世界 🚀', '16px Inter')
const { height, lineCount } = layout(prepared, 320, 20)

要每一行的内容:

const prepared = prepareWithSegments(text, '16px Inter')
const { lines } = layoutWithLines(prepared, 320, 20)

layoutNextLine() 更有意思 — 每行可以设不同宽度。文字可以环绕图片流动,像杂志排版。


和 AI 编程有什么关系

Cheng Lou 设计 API 时说要 “AI-friendly”。这不是空话。

我之前在 Claude Code 源码分析里看到一个核心问题:AI agent 在编辑器里每改一行,工具调用 → 返回结果 → UI 更新,这个 loop 的流畅度直接决定编程体验。agent-driven 界面更极端 — UI 不是人提前设计的,是 AI 实时组装的,每次输出变化都触发布局更新。

现在大多数 agent UI 的卡顿来自网络延迟和 LLM token 流,不是浏览器布局。但对那些实时生成复杂界面的 agent 工具来说,布局计算的可预测性会成为真正的瓶颈。

具体到”编辑器与测试 loop”:AI 写代码 → 跑测试 → 展示结果 → 改代码。如果结果展示区要频繁重排大量文本(测试输出、diff 视图、日志流),每次都走 DOM reflow 就是在给已经够慢的 loop 加不必要的延迟。Pretext 让这部分几乎免费。

另一个容易被忽略的角度:开发时验证。AI 写的代码里按钮文字会不会溢出到第二行?以前靠人肉看或 DOM 测量,现在可以用 Pretext 在没有浏览器的环境里验证。CI/CD 和自动化测试里直接用。


局限

  • 只能在浏览器跑,服务端渲染还没做
  • system-ui 字体在 macOS 上 Canvas 和 DOM 解析出不同光学变体,得用命名字体
  • 非常窄的容器里断词位置可能出人意料
  • 库才发布几天,生产环境要谨慎

争议

Domenic Denicola(Web 标准贡献者)批评说这只是 Canvas measureText() 的包装。

技术上没错,底层确实是 Canvas API。但把”正确匹配三大浏览器换行行为 + 处理所有国际化边界情况”叫做”包装”,就像把 React 叫做 document.createElement() 的包装。

真正有意思的问题在别处:CSS Exclusions 和 CSS Regions 规范写了多年,浏览器厂商就是不实现。Pretext 填的恰恰是这个空白 — 规范存在,实现不存在。如果 Pretext 够成功,反而可能降低浏览器厂商实现这些规范的动力。一种奇怪的”成功”。


我的判断

大多数网站不需要这东西。博客、落地页、电商站,CSS 完全够用。

值得关注的是这些场景:

  1. 聊天应用 — 虚拟滚动 + 气泡精确宽度
  2. Agent 驱动的 UI — AI 实时生成复杂界面时的布局性能
  3. 编辑器/IDE — 代码 diff、测试输出、日志流的频繁重排
  4. CI 测试 — 无浏览器环境验证文本布局

Cheng Lou 之前做过 Reason/ReScript,对开发者工具的品味一直不错。48 小时 13k stars 不是偶然 — 它解决了一个真实存在但大家习惯了忍受的痛点。布局计算从 DOM reflow 降到纯算术,是在不该有等待的地方省下了等待。


参考: