🚀24k Star 的 Pretext 为何突然爆火:它不是排版库,而是在重写 Web 文本测量

0 阅读11分钟

前端做聊天气泡、瀑布流、富文本卡片和动态排版时,最难的往往不是把字显示出来,而是提前知道文本会占多高。传统方案通常依赖 DOM 读值,性能和准确性都容易出问题。Pretext 把这件事拆成可预计算和可复用两段,给出了可跑、可测、可验证的文本布局方案。本文会把它的核心思路、关键 API、适用场景、边界条件和落地方法一次讲透。

屏幕截图 2026-03-31 164418.png

大家好,我是 iDao。10 年全栈开发,做过架构、运维,也在落地 AI 工程化。这里不搞虚的,只分享能直接跑、能直接用的代码、方案和经验。内容包括:全栈开发实战、系统搭建、可视化大屏、自动化部署、AI 应用、私有化部署等。关注我,一起写能落地的代码,做能上线的项目。

一、为什么这个项目突然被大量前端盯上了

问题现象

你只要做过下面这些界面,基本都碰过同一类问题:

  • 聊天消息高度要先算出来,虚拟列表不能靠猜
  • 标题要绕开图片或障碍物,CSS 做不到业务想要的效果
  • 多语言按钮文案切换后,偶发换行导致布局抖动
  • 瀑布流卡片高度依赖真实渲染结果,滚动时频繁读 DOM

根因分析

很多项目现在还在用 getBoundingClientRect()offsetHeight 这类 DOM 读值去拿文本高度。单次看没什么,一旦和样式写入、状态更新交错,浏览器就可能被迫同步做 layout 和 reflow。这类开销在长列表、富文本、响应式场景里会非常明显。

Pretext 的定位很直接:它不是一个简单的排版小工具,而是一个“绕开 DOM 测量热路径”的文本布局库。它的核心目标不是把文字画出来,而是让你在不依赖实时 DOM 读值的前提下,稳定预测文本布局结果。

解决步骤

官方仓库给出的描述是:

  • 纯 JavaScript/TypeScript 的 multiline text measurement 与 layout 库
  • 支持多语言、emoji、混合双向文本
  • 核心目标是绕开 DOM 测量,例如 getBoundingClientRectoffsetHeight

安装很简单:

npm install @chenglou/pretext

验证方式

这个项目当前 GitHub 星标已经超过 24k,说明它击中的不是边缘需求,而是前端长期存在、但一直没有被优雅解决的问题。


二、它最值钱的设计,不是“算宽度”,而是把热路径拆干净了

问题现象

不少文本测量方案也能算宽度、算高度,但一到窗口 resize、容器宽度变化、多语言切换,性能就开始掉。原因通常不是算法本身,而是每次变化都把整段文本重新测一遍。

根因分析

文本布局其实包含两类成本:

  • 一次性成本:分段、空白处理、Canvas 测宽、缓存
  • 高频成本:容器宽度变化后重新计算行数和高度

如果这两类成本混在一起,任何 resize 都会把重活重新做一遍。

解决步骤

Pretext 的核心 API 是两段式:

import { prepare, layout } from '@chenglou/pretext'

const prepared = prepare('AGI 春天到了. بدأت الرحلة 🚀', '16px Inter')
const { height, lineCount } = layout(prepared, 320, 20)

console.log(height, lineCount)

这里的分工很重要:

  • prepare():做一次性分析和测量
  • layout():只基于缓存结果做纯算术布局

官方文档明确说明,不要在同样的文本和配置上反复执行 prepare()。例如窗口宽度变化时,应该只重新执行 layout()

关键参数说明

有 3 个参数必须认真对齐:

  • font 这里不是随便传一个字号字符串,而是要和真实 CSS font 简写保持一致,包括字号、字重、字体族
  • maxWidth 传入的是文本真实可用宽度,不是父容器的大概宽度
  • lineHeight 必须和页面里真实使用的 line-height 一致,否则高度一定会偏

验证方式

官方 README 给出的当前基准快照里:

  • prepare() 处理共享的 500 段文本,大约是 19ms
  • layout() 对同样批次大约是 0.09ms

这个数据最关键的意义,不是“某个绝对值很快”,而是它把重活放在前面,把热路径做轻了。


三、真正让它和普通测量库拉开差距的,是第二组 API

问题现象

很多业务不是只想知道“这一段文本多高”,而是想知道“每一行怎么断、怎么排、能不能自己控制”。

比如:

  • 消息气泡希望在不增加行数的情况下尽量缩窄宽度
  • 标题要绕开图片走不同宽度的路径
  • Canvas、SVG、WebGL 场景要自己控制逐行绘制
  • 富文本里 inline chip、链接、代码片段要一起参与布局

根因分析

传统 DOM 方案通常只能拿到最终结果,很难把布局过程作为业务可用的 API 暴露出来。你能拿到高度,但拿不到每一行的断点、宽度、游标位置,更别说按变化宽度逐行布局。

解决步骤

Pretext 额外提供了一组更强的 API:

  • prepareWithSegments()
  • layoutWithLines()
  • walkLineRanges()
  • layoutNextLine()

例如逐行按变化宽度布局:

import { prepareWithSegments, layoutNextLine } from '@chenglou/pretext'

const prepared = prepareWithSegments(
  'A floated image changes each line width',
  '16px Inter'
)

let cursor = { segmentIndex: 0, graphemeIndex: 0 }
let y = 0

while (true) {
  const width = y < 120 ? 240 : 360
  const line = layoutNextLine(prepared, cursor, width)
  if (line === null) break

  console.log(line.text, line.width)
  cursor = line.end
  y += 24
}

这类能力意味着它不只是服务 DOM,也能服务 Canvas、SVG,甚至未来更适合服务端布局场景。

验证方式

案例站点已经把这些能力做成了可直接观察结果的页面,包括:

  • Accordion
  • Bubbles
  • Dynamic Layout
  • Editorial Engine
  • Masonry
  • Rich Text
  • Justification Comparison

这点很重要。它不是 README 里的纸面 API,而是已经对应到具体可见的布局效果。


四、这个项目最厉害的地方,不是“有想法”,而是它做了足够重的验证

问题现象

文本布局最怕的,不是功能不够,而是看起来能用,实际一上多语言、多字体、多浏览器就开始错。中文、日文、阿拉伯文、泰文、缅甸文、emoji、软连字符、混合双向文本,一旦混在一起,很多局部优化都会失效。

根因分析

文本布局不是一个单纯算法题。它背后混着:

  • 浏览器字体引擎差异
  • 各语言的断行规则
  • 空白处理
  • 标点粘连规则
  • emoji 和 grapheme 行为
  • 浏览器特定 quirks

也正因为这样,很多“看起来更准”的方案,在真实浏览器和真实语料里并不一定成立。

解决步骤

Pretext 的研究日志里有几个结论非常有参考价值:

  1. layout() 必须保持 arithmetic-only也就是热路径里不回头做 DOM 读值,不回头重测完整字符串。

  2. 更可靠的修正,优先放在 prepare()包括预处理、标点 glue 规则、空白处理、分段策略,而不是把逻辑越堆越多地塞回热路径。

  3. 有些路线试过之后被明确放弃了比如:

    • layout() 中重建字符串再测量
    • 用隐藏 DOM 做准备阶段测量
    • 用 SVG getComputedTextLength()
    • 把更多“聪明逻辑”塞回高频路径
  4. system-ui 不是安全选择 官方研究明确指出,在 macOS 上,Canvas 和 DOM 对 system-ui 的解析可能不一致。要追求准确性,应使用命名字体。

验证方式

这个项目并不是“作者说它准”,而是把验证体系做出来了。开发脚本里能看到一整套校验流程:

bun install
bun start
bun run check
bun run accuracy-check
bun run benchmark-check
bun run pre-wrap-check
bun run corpus-check

这意味着:

  • 有浏览器准确性校验
  • 有性能基准校验
  • 有语料回归校验
  • 有特定模式,例如 pre-wrap 的专项验证

这类工程化验证,才是这个项目真正值得高看一眼的地方。


五、它最先适合落地的,不是“花哨排版”,而是三类高回报场景

问题现象

很多人第一次看到 Dynamic Layout、Editorial Engine 这种 demo,会先被视觉效果吸引。但大多数团队最先能吃到收益的,并不是这些高级排版,而是那些本来就会频繁测量文本高度的普通业务组件。

根因分析

高回报场景的共性很简单:

  • 文本多
  • 尺寸变化频繁
  • 现在依赖 DOM 读值
  • 一旦卡顿,用户感知很强

解决步骤

我更建议优先从下面 3 类场景试:

1. 虚拟列表和消息流

例如 IM、评论流、通知流。文本高度如果能提前算出来,就能减少滚动过程中的实时测量和反复布局。

2. 聊天气泡和多行卡片

案例页里的 Bubbles 非常典型。它展示的不是“消息能换行”,而是“能在保持行数不变的前提下,把宽度收得更紧”。

3. 富文本卡片和编辑器周边布局

例如标签、链接、代码片段、chips 和正文混排。Pretext 的 richer layout API 更适合做这类需要细粒度控制的场景。

验证方式

最简单的验证方法,不是先接整个项目,而是拿一个你现在最依赖 DOM 测量的组件做 A/B 对比:

  • 旧方案:getBoundingClientRect()offsetHeight
  • 新方案:同一字体和行高下,预先 prepare(),宽度变化时只 layout()

对比下面这些行为:

  • resize 时是否更稳
  • 长列表滚动时是否更顺
  • 多语言切换时布局抖动是否减少
  • 是否更容易做高度预测和虚拟化

常见报错和解决建议

报错 1:测出来的高度和真实页面不一致

原因

  • font 参数和真实 CSS 不一致
  • lineHeight 传错
  • 在 macOS 上用了 system-ui

解决

  • 用命名字体,例如 16px Inter
  • 确保 line-height 用真实值
  • 不要用模糊估算值替代真实样式参数

报错 2:textarea 内容里的空格、Tab、换行被吞掉

原因

默认模式是面向 white-space: normal 的,不会保留普通空格和硬换行。

解决

const prepared = prepare(textareaValue, '16px Inter', {
  whiteSpace: 'pre-wrap',
})

这个模式是 0.0.2 版本新增的,专门用于 textarea-like 文本。

报错 3:项目接上以后还是慢

原因

你把 prepare() 放进了高频路径,每次宽度变化都重新做一次。

解决

同一份文本和字体配置只做一次 prepare(),后续宽度变化只调用 layout()


常见坑

  • 把它当成完整字体渲染引擎。不是。它当前目标明确是常见网页文本布局,而不是全量字体引擎替代品。
  • system-ui 追求精确测量。官方研究已经明确提示,在 macOS 上这并不可靠。
  • 忽略默认断行前提。它当前对齐的常见文本模型包括 white-space: normalword-break: normaloverflow-wrap: break-wordline-break: auto
  • 把 demo 当成唯一价值。项目真正最先能落地的地方,往往是虚拟列表、卡片高度预测、消息流和富文本布局。
  • 只看 README,不看研究日志。这个项目真正稀缺的部分,不是 API 名字,而是它公开了哪些路试过、哪些路放弃了。

快速自检清单

  • 你的 font 是否和真实 CSS 完全一致
  • 你的 lineHeight 是否来自真实样式值
  • 同一段文本是否只 prepare() 了一次
  • textarea 类内容是否开启了 whiteSpace: 'pre-wrap'
  • macOS 场景是否避免使用 system-ui
  • 是否先用小组件试点,而不是一次性重构整套排版逻辑

今天就能做的下一步

今天不要先想着“重构整个排版引擎”。更现实的动作是:

  1. 找出一个现在最依赖 DOM 测量的组件例如消息气泡、卡片摘要、按钮文案校验
  2. 保持视觉层不动只替换“文本高度预测”这一层
  3. 做一次小范围对比 看 resize、滚动、多语言切换时是否更稳

如果这一步能跑通,你再考虑把它逐步扩到消息流、虚拟列表或富文本卡片场景。

收尾

Pretext 这波真正值得关注的,不是“又一个排版库”,而是它把文本测量从浏览器临时求值,拆成了可预计算、可缓存、可验证的工程模型。对做复杂前端的人来说,这个方向比一个新 API 更重要。它未必会替代所有布局方案,但已经足够成为高性能文本 UI 的一个底层能力候选。

关注 【iDao技术魔方】,获取更多全栈到AI可落地的实战干货。