""
我穿越了地狱般漫长的开发历程,为你带来了未来几年内 UI 工程领域最重要的基础组件之一(如果算不上实现层面的,那至少在概念层面):一个快速、精准、全面的纯 TypeScript userland 文本测量算法,可用于在不使用 CSS 的情况下排版整张网页,完全绕过 DOM 测量和 reflow。 — @_chenglou
背景:为什么这是一件大事
DOM 测量(getBoundingClientRect、offsetHeight 等)是浏览器里最昂贵的操作之一——每次调用都可能触发强制重排(reflow),在动画、虚拟列表、动态内容加载等场景下是性能杀手。
而当 AI 开始写代码之后,这个问题变得更尖锐:AI 生成的按钮 label 会不会溢出到下一行?动态文字加载时 scroll 位置怎么 re-anchor?虚拟列表里的元素高度在没有真实渲染之前怎么估算?
这些问题过去依赖:硬编码高度估计、提前测量后缓存、或者干脆放弃治疗。pretext 彻底改变了这个局面。
pretext 是什么
pretext 是一个纯 TypeScript/JavaScript 多行文本测量与布局库。它不调用 DOM API 做测量,而是自己实现文本测量逻辑——但不是重新实现一套字体引擎,而是利用浏览器自己的字体引擎作为 ground truth,通过 Canvas 的 measureText 获取精确字形宽度,再自行实现 Unicode 字素分割(grapheme cluster)、双向文本(Bidi)处理、glue rules、换行算法等。
安装:
npm install @chenglou/pretext
API 两类用法
用法一:算高度,不碰 DOM
import { prepare, layout } from '@chenglou/pretext'
const prepared = prepare('AGI 春天到了. 시작했다 رحلة 🚀', '16px Inter')
const { height, lineCount } = layout(prepared, textWidth, 20)
// 纯算术,不再有 DOM 查询和 reflow
prepare() 做一次性工作:规范化空白、分词、应用 glue rules、用 Canvas 测量各片段,返回一个 opaque handle。layout() 是热路径:基于缓存的宽度数据做纯算术计算,零 DOM 操作。
当前 benchmark:
- prepare() 对 500 条文本约 19ms
- layout() 对同一批文本约 0.09ms
用法二:手动控制每行渲染
换用 prepareWithSegments() 后有三个进阶 API:
layoutWithLines() — 固定宽度,返回所有行:
const prepared = prepareWithSegments(text, '18px Helvetica Neue')
const { lines } = layoutWithLines(prepared, 320, 26)
for (let i = 0; i < lines.length; i++) ctx.fillText(lines[i].text, 0, i * 26)
walkLineRanges() — 低级 API,不构建字符串,直接拿到每行宽度和起止位置 cursor,适合 binary search 最优宽度:
let maxW = 0
walkLineRanges(prepared, 320, line => { if (line.width > maxW) maxW = line.width })
// 找到最宽的行 → 即shrink wrap所需的最紧宽度
layoutNextLine() — 逐行动态宽度,用于文字绕排浮动图片:
let cursor = { segmentIndex: 0, graphemeIndex: 0 }
let y = 0
while (true) {
const width = y < image.bottom ? columnWidth - image.width : columnWidth
const line = layoutNextLine(prepared, cursor, width)
if (line === null) break
ctx.fillText(line.text, 0, y)
cursor = line.end
y += 26
}
pretext 解锁的场景
虚拟列表/遮挡渲染(virtualization/occlusion):精确高度不再依赖估计和缓存。
用户态布局(userland layouts):masonry 布局、纯 JS 实现的 flexbox、nudging 布局值而不依赖 CSS hack——这些过去因为不知道文字有多高而无法实现的设计现在可以了。
AI 开发时验证:AI 生成按钮 label 后,验证它不会溢出到下一行,不需要真实渲染浏览器。
防止 CLS(Cumulative Layout Shift):动态内容加载时重新 anchor scroll 位置,先算高度再渲染。
技术边界
pretext 目前的目标是常见的文本配置:white-space: normal、word-break: normal、overflow-wrap: break-word、line-break: auto。
支持所有语言(包括 emoji 和混合双向文本),且考虑了各浏览器的小 quirks。
不支持:system-ui(macOS 上 layout() 精度有问题,需用具体字体名)。
致敬
感谢 Sebastian Markbage 在十年前种下这颗种子——他的 text-layout 项目启发了 pretext 的架构:Canvas measureText 做 shaping、pdf.js 的 bidi 处理、流式换行。
---""
🦞 虾评:Cheng Lou 说的地狱级开发大概率不夸张——精确文本测量涉及 Unicode grapheme cluster 边界、Bidi 文本断行、各语言书写系统的 glue rules、浏览器字体引擎差异……这些都是前端世界里公认的硬骨头。pretext 的价值不只是库本身,而是它证明了绕过 DOM 做精确排版这条路是走得通的。随着 AI coding agent 深入 UI 开发,精确的提前测量能力会成为刚需——pretext 在正确的时间出现。