浏览器里最贵的操作是什么?不是网络请求,不是图片解码,是布局重排(layout reflow)。每次你用 getBoundingClientRect 获取元素尺寸,浏览器就要重新计算整个页面的几何信息。一个复杂页面里连续调几十次,帧率直接崩给你看。
React 核心团队成员陈天龙(Cheng Lou)最近开源的 Pretext,把这个痛点连根拔了。12.9k Star,478 个 Fork,这个纯 JavaScript/TypeScript 库用一套自研的文本测量逻辑,彻底绕过 DOM API。
为什么 DOM 测量这么贵
传统做法里,你要算一段文字占多高,得先把文字塞进 DOM,让浏览器渲染,再读尺寸。这个"读"的动作会强制同步布局——浏览器不得不暂停手头所有工作,先把你这行字算清楚。
Pretext 的思路像提前备菜。prepare() 函数一次性做完所有脏活:标准化空白字符、按语言规则分段、用 Canvas 测量每个片段宽度。返回一个"预制菜"句柄。之后无论换行还是改容器宽度,layout() 只做纯算术,零 DOM 接触。
陈天龙在 README 里写得很直白:「Do not rerun prepare() for the same text and configs; that'd defeat its precomputation.」重复准备会抵消预计算的优势,resize 场景里只调 layout 就行。
多语言支持不是附赠,是基本功
文本布局库最容易翻车的是国际化。阿拉伯语从右往左,泰语没有空格分词,emoji 可能占两个字符宽度,中日韩混排时标点挤压规则各不相同。
Pretext 的测试语料库(corpora)目录里塞了各种极端案例。README 里那句「supports all the languages you didn't even know about」不是夸张——它确实处理了混合双向文本(mixed-bidi)、变宽 emoji、以及 pre-wrap 模式下保留制表符和换行符的 textarea 行为。
用法上,标准文本和类 textarea 文本只差一个配置对象:
标准文本(white-space: normal 行为):
const prepared = prepare('AGI 春天到了. بدأت الرحلة ', '16px Inter')const { height, lineCount } = layout(prepared, textWidth, 20)保留空白字符(textarea 行为):
const prepared = prepare(textareaValue, '16px Inter', { whiteSpace: 'pre-wrap' })const { height } = layout(prepared, textareaWidth, 20)
AI 时代的迭代友好设计
陈天龙在简介里埋了一个有趣的点:「very AI-friendly iteration method」。Pretext 的测量逻辑完全可预测、无副作用,LLM 生成代码时不容易踩到 DOM 时序的坑。
传统 DOM 测量代码里,新手常犯的错误是在组件还没 mount 时就读尺寸,或者忘了监听字体加载事件。Pretext 把"浏览器字体引擎作为唯一真相源",所有测量走 Canvas 2D 上下文,行为稳定到可以写进单元测试。
输出目标也很全:DOM、Canvas、SVG 都已支持,服务端渲染(server-side)正在开发中。这意味着同一套布局逻辑可以跑在 Node.js 里做首屏预计算,浏览器里只做复现。
性能数字与真实场景
仓库里带了一个 benchmark 目录,当前快照显示的性能提升——陈天龙没给具体百分比,但强调"checked-in benchmark snapshot"可以复现。从架构原理推断,避开强制同步布局的收益在复杂列表、虚拟滚动、实时换行预览等场景最显著。
一个典型用例是富文本编辑器的行高计算。用户输入时,每敲一个字就要重新算后续所有行的位置。用 Pretext 的话,输入内容变才调 prepare,光标移动或容器宽度变只调 layout,两条路径成本差一个数量级。
另一个场景是响应式图表的标签布局。D3 或 ECharts 里经常需要算文字会不会溢出,传统做法要反复创建临时 DOM 节点。Pretext 让这套逻辑可以纯内存运行,甚至提前在 Web Worker 里算好。
陈天龙把开发笔记(thoughts.md)、研究文档(RESEARCH.md)、状态追踪(STATUS.md)全部开源。AGENTS.md 和 CLAUDE.md 看起来是给 AI 编码助手写的上下文指南——这种"为 AI 协作而设计"的文档结构,本身也是一种产品信号。
安装很简单:npm install @chenglou/pretext。 demos 目录跑起来需要 Bun,但核心库不绑定特定运行时。
这个项目最狠的地方在于——它没有发明新布局算法,而是把浏览器已经会做的事,用可缓存、可预测的方式重新实现了一遍。代价是 prepare 那一步的预计算,收益是之后无数次的零成本 layout。
如果你的产品里有大量动态文本布局,现在会怎么选?继续用 DOM API 赌浏览器优化,还是换一套确定性更高的方案?
热门跟贴