首页 > 基础资料 博客日记
Pretext:值得关注的文本排版引擎
2026-03-30 18:32:45基础资料围观2次
Pretext 是一个用 TypeScript 实现的用于多行文本精确测量和布局的引擎。不碰 DOM,不触发 reflow,却能完美匹配浏览器字体引擎在各种语言、emoji、混合文字方向下的真实表现。它刚刚发布,发展势头很猛。
我觉得这是过去十年里最值得关注的文本引擎之一。它不是小打小闹的优化,而是把文本这块一直卡着大家脖子的核心问题彻底解决掉了。

先说清楚它解决什么痛点。网页上但凡涉及动态文本,比如聊天消息、文章卡片、虚拟列表、自动换行输入框、响应式排版,你都得问浏览器“这个文本在多宽容器里到底占多少高”。过去唯一办法是扔进 DOM,读 getBoundingClientRect 或者 offsetHeight,然后立刻写回样式。这操作直接触发布局重排,浏览器要重新计算整个页面流式布局,尤其在长列表、频繁 resize、AI 生成内容实时流式输出的时候,性能直接雪崩。很多框架只好批量测量、缓存估算值、妥协精度,结果就是滚动卡顿、布局偏移、虚拟化高度猜不准、画布渲染对不上 CSS 样式。Pretext 把这一切全绕过去:一次 prepare 预计算,后面 layout 全是纯算术,毫秒级出结果,还跨浏览器一致。
它体积极小,支持所有主流语言,包括 CJK、阿拉伯文、希伯来文、emoji、混合 bidi,还特别处理了 Safari、Chrome、Firefox 各自的换行 quirks。作者 Cheng Lou 之前在 React、ReasonML、ReScript 这些项目里摸爬滚打,又在 Midjourney 干过视觉生成,对文本排版的需求理解得透彻。他说这是“爬过地狱”才搞出来的基础件,我完全相信。因为文本测量看似简单,实际是浏览器字体引擎 + Unicode 规范 + 各家实现差异的超级复杂组合,普通人根本遭不住。
核心 API 拆解与使用
库暴露两套 API,一套给“只需要高度”的场景,另一套给“手动控制每一行”的高级玩法。
先看简单那套,绝大多数业务场景够用:
import { prepare, layout } from '@chenglou/pretext'
const prepared = prepare('AGI 春天到了. بدأت الرحلة 🚀', '16px Inter')
const { height, lineCount } = layout(prepared, 300, 24) // 宽度 300px,行高 24px
prepare 做一次性重活:规范化空白、按语义分割文本、应用换行规则、用 Canvas 测量每段宽度、缓存结果,返回一个不透明句柄。layout 就纯计算,输入最大宽度和行高,立刻吐高度和行数。注意 font 参数必须和 CSS 里 font 声明完全一致(大小、字重、家族),行高也要对齐,不然精度会飘。
如果你的文本是 textarea 那种,要保留空格、tab、硬换行可见,就加选项:
const prepared = prepare(textareaValue, '16px Inter', { whiteSpace: 'pre-wrap' })
性能数据(官方基准):prepare 对 500 条混合文本批次大概 19ms,layout 同一批次只要 0.09ms。实际项目里,你在组件 mount 时 prepare 一次,resize 或宽度变化时只跑 layout,帧率直接起飞。
以前用 DOM 测量 + requestAnimationFrame 节流,1000 条动态高度消息列表滚动还偶尔掉帧。现在换 Pretext,虚拟化逻辑简化成线性遍历高度缓存,120fps 稳得一批。
高级 API 更狠,面向 Canvas、SVG、WebGL、甚至未来服务端渲染:
import { prepareWithSegments, layoutWithLines, walkLineRanges, layoutNextLine } from '@chenglou/pretext'
const prepared = prepareWithSegments(text, '18px "Helvetica Neue"')
const { lines } = layoutWithLines(prepared, 320, 26) // 固定宽度返回所有行信息
for (const line of lines) {
ctx.fillText(line.text, 0, y)
y += 26
}
prepareWithSegments 返回带段信息的结构。layoutWithLines 直接给你每行完整文本和宽度。walkLineRanges 更底层,只给宽度和光标位置,不拼接字符串,适合二分搜索最优容器宽度(比如聊天气泡 shrinkwrap)。layoutNextLine 是迭代器,能处理变宽场景,比如文字绕图、杂志多栏、浮动元素:
let cursor = { segmentIndex: 0, graphemeIndex: 0 }
let y = 0
while (true) {
const width = (y < imageBottom) ? columnWidth - imageWidth : columnWidth
const line = layoutNextLine(prepared, cursor, width)
if (!line) break
// 渲染 line.text
cursor = line.end
y += lineHeight
}
这些 API 把文本从“黑盒”变成“可编程对象”。以前 CSS 只能给你最终渲染结果,现在你能提前知道每一行的精确边界、宽度、断点,想怎么玩就怎么玩。
它如何做到精确又快
Pretext 的源码干净到几乎没有废代码(整个库打包后几 KB)。核心思路是用浏览器自己的 Canvas 作为真理源头,预先把文本拆成最小可测量单元,缓存宽度,然后用纯 JS 实现换行逻辑。
prepare 阶段先用 Intl.Segmenter 把文本按 grapheme 切分(不是简单 char,因为 emoji 和组合字符要当一个单元处理)。同时处理 Unicode 空白规则、bidi 方向。接着对每段用 Canvas.measureText 测宽度,缓存下来。作者特别提到用了 AI(Claude Code)迭代几周,对着浏览器真实渲染结果反复比对,确保每一种容器宽度下的换行行为 100% 一致。这就是为什么它能跨浏览器精确:不是自己造轮子,而是“模仿”浏览器引擎最准确的那部分,再把测量结果前置缓存。
布局阶段完全抛弃 DOM。line breaking 算法基于缓存的段宽度,做贪心 + 必要回溯,处理连字符、标点禁则、CJK 不换行等所有边缘 case。walkLineRanges 和 layoutNextLine 内部维护一个 cursor(segmentIndex + graphemeIndex),每次推进时只算当前行能塞多少段,算完立即返回,避免任何字符串拼接开销。
我特别欣赏它对 whiteSpace 的处理。normal 模式下自动合并空白、忽略换行;pre-wrap 模式保留原始格式,却依然用同一套测量引擎。bidi 混合文字(英文 + 阿拉伯文)也无缝,因为它在分割时就考虑了方向性。
缓存机制是性能王牌。clearCache() 可以手动清理,适合频繁切换字体的大应用。所有测量结果共享,多次 prepare 相同文本和字体直接复用。官方 benchmark 里 layout 0.09ms 不是吹的,因为它就是数组索引 + 加减法。
对比以前手写 Canvas 文本布局的痛苦(自己测宽度、自己算换行、自己处理 ellipsis、自己对齐浏览器 quirks),Pretext 直接把这些脏活全包了。你只需要专注业务逻辑。
不止性能
-
虚拟化列表。以前 variable height 虚拟列表要么估高、要么 DOM 测量后强制 rerender,滚动时容易闪。现在 Pretext 提前算出所有高度,occlusion culling 变成线性扫描,Masonry 那种不规则网格也能 120fps。演示里几十万文本框滚动丝滑,就是证明。
-
布局偏移(CLS)彻底消失。新文本加载时,不再需要等 DOM 渲染再调整锚点。高度已知,scroll position 直接算。
-
Canvas / SVG / WebGL 渲染一致性。以前 DOM 文字和 Canvas 文字永远对不齐,现在同一份 prepared 数据,两边渲染结果像素级一致。游戏 UI、数据可视化、设计工具直接起飞。
-
响应式高级排版。聊天气泡自动 shrinkwrap、最优宽度二分搜索、杂志式多栏动态流式、文字绕图、variable font ASCII art……以前 CSS 做这些要一堆 hack,现在一行 layout 调用搞定。演示里的动态布局 demo,像印出来的杂志,却能实时拖拽窗口自适应。
-
编辑器与 AI 内容生成。实时流式输出时,不再卡在测量循环。drop cap、justification(Knuth-Plass 算法已有人基于 Pretext 实现)、自动平衡文本、widows/orphans 处理,全都可编程。社区已经有人做爆炸文字、倾斜阅读、3D 排版、手写动画流式渲染,创意直接爆炸。
-
开发体验与 AI 协同。作者设计时就考虑了 AI 友好:API 极简,测量结果可预测。以后 AI 写 UI 组件时,不用再猜文本高度,直接调用 Pretext 验证不溢出。
不是取代 CSS
很多人问:这不就是把 CSS 活抢了吗?比如 CSS Shapes,不是。CSS 依然负责最终绘制(如果你用 DOM 渲染),Pretext 只管测量和布局决策。你依然可以用 Tailwind、Flex、Grid 搭框架,但关键的文本高度现在可控了。未来服务端渲染支持后,甚至能把整页排版算好再吐 HTML,hydration 零不匹配。
CSS 的文本模块从来就不是为“精确可编程”设计的。它是声明式、黑盒、浏览器实现的。Pretext 把控制权还给 JS,让你能做 CSS 做不到的:变宽迭代、精确 shrinkwrap、跨平台一致、非矩形流式。两者结合,才是未来。
当然,它不是万能药。目前不直接支持 selection 样式(但演示里已能 overlay 透明 canvas 画选区),复杂排版规则如 full justification 的高级微调还在社区扩展中。但作者开放态度明显,TODO 里写着 server-side、更多语言支持,欢迎 PR。
结论
Pretext 不是一个库,而是一次前端文本能力的重启。它证明了:把浏览器字体引擎的真相前置缓存 + 纯 JS 布局,就能干掉 DOM reflow 这个老大难。作者用 AI 迭代对齐浏览器行为,这条路以后会成为标配。
对普通开发者,它让你写更丝滑的列表、更稳的编辑器;对设计师,它打开了杂志级响应式排版;对 AI 时代,它把文本从“渲染后才知道”变成“生成前就知道”,让智能界面真正智能起来。
我强烈建议用 AI 玩一玩 demos。文本不再是瓶颈,而是创意的起点。
参考资料
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:jacktools123@163.com进行投诉反馈,一经查实,立即删除!
标签:

