GitHub Star 暴涨!前 React 核心成员出手,把浏览器 30 年算不好的文字布局问题解决了

张开发
2026/4/6 11:21:23 15 分钟阅读

分享文章

GitHub Star 暴涨!前 React 核心成员出手,把浏览器 30 年算不好的文字布局问题解决了
做前端的人多少都被一个问题折磨过你没法在不渲染的情况下知道一段文字有多高。想做虚拟滚动得先把元素塞进 DOM量一下高度再拿出来。想做聊天气泡自适应宽度fit-content撑出来的宽度永远比你想要的多几个像素。想做瀑布流每张卡片都要等浏览器算完才能定位中间那段闪烁谁都没办法。这些问题的共同根源只有一个浏览器的文字测量机制是 30 年前为静态文档设计的压根没考虑过今天的动态场景。上周前 React 核心团队成员 Cheng Lou 开源了一个叫Pretext的库用纯 TypeScript 实现了一套文本测量和排版算法。不走 DOM不触发重排纯数学计算。GitHub 星标已经突破 1.6 万推特原帖浏览量超过 1900 万。前端圈很久没有这么热闹了。先说清楚它解决的核心痛点前端工程师测量文字尺寸标准做法是调用getBoundingClientRect()或读取offsetHeight。这两个 API 有一个致命问题它们会强制浏览器执行一次同步布局计算。什么意思浏览器本来是攒一批 DOM 变更最后统一算一次布局。但你一调用这些 API它就不得不立刻把所有挂起的变更算完把结果给你。这就是所谓的强制重排Forced Reflow。一次两次还好。但实际业务中你可能需要连续测量几十甚至几百个文本块的高度。每次测量都触发一次全量重排。Chrome DevTools 里那一排红色的Layout标记看着就心烦。Pretext 的做法很直接它自己实现了一套文字排版的数学模型调用浏览器底层的字体引擎拿到字形数据但完全不碰 DOM。你给它文字和字体配置它用纯算术告诉你结果。两行代码看懂基本用法import { prepare, layout } from chenglou/pretext // 第一步预处理文字和字体信息一次性开销 const prepared prepare(你的文字内容, 16px Inter) // 第二步给定容器宽度算出高度和行数 const { height, lineCount } layout(prepared, 320, 26)prepare()是一次性的预处理500 个文本块大约耗时 19ms。之后每次调layout()只需要 0.09ms而且因为是纯数学运算不会阻塞主线程不会触发重排。对比一下用 DOM 测量同样 500 个文本块每次调用getBoundingClientRect()大约 30ms总共触发 500 次重排。两者的差距不是百分比级别的优化是量级上的碾压。真正有意思的是行级控制如果 Pretext 只是快速测高度那它顶多是个性能优化工具。真正让前端社区兴奋的是它暴露出的行级排版能力。它提供了三个进阶 APIlayoutWithLines()返回每一行的具体内容和宽度。你可以用这个做自定义的文字渲染把结果画到 Canvas 上或者用 SVG 排版。walkLineRanges()只返回每行的位置和宽度不构建文本字符串性能更好。关键用途是实现多行文本的精确收缩包裹。CSS 里的fit-content大家都用过它的逻辑是找最宽的那行用这个宽度撑开整个容器。结果就是短行右边留了一大片空白。Pretext 可以用二分查找找到让行数保持不变的最窄宽度实现零冗余像素的包裹。Cheng Lou 专门做了一个聊天气泡的对比 Demo左边是 CSSfit-content的效果右边是 Pretext 收缩包裹的效果差距一目了然Pretext 聊天气泡收缩包裹 Demo这个能力听起来小但对聊天界面、卡片布局、标签排列这些场景来说意味着视觉精度的质变。layoutNextLine()支持逐行处理而且每行可以指定不同的宽度。这意味着你可以实现杂志那种图文绕排的效果文字遇到图片时自动收窄图片过去之后宽度恢复。这在纯 CSS 里需要 CSS Shapes兼容性和灵活度都不够用。他还展示了一个多栏杂志布局的 Demo响应式、动态排版内容完全在 JavaScript 层面排好再交给浏览器绘制效果堪比专业排版软件Pretext 多栏杂志布局 Demo哪些场景能直接受益说几个实际的应用场景虚拟列表和虚拟滚动。现有方案比如react-virtualized要么用固定高度要么用估算高度 滚动后修正。Pretext 可以在渲染前精确计算每个条目的高度滚动条不会跳列表不会闪。AI 对话界面。流式输出的文字需要不断更新气泡高度。每次 token 进来都getBoundingClientRect一次页面卡顿肉眼可见。Pretext 让你在 JavaScript 层面算好高度DOM 只管渲染。富文本编辑器。光标定位、选区计算、行高预测这些编辑器的核心能力全都依赖精确的文字测量。用 DOM 测量的编辑器在大文档里性能急剧下降Pretext 提供了一条绕过瓶颈的路径。响应式布局预计算。需要根据不同屏幕宽度决定文字是两行还是三行以前只能真的渲染出来看。现在可以在 JS 里直接算。它不能做什么Pretext 的文档写得很实在明确列出了自己的边界它只支持标准的文本配置。white-space只支持normal和pre-wrapword-break只支持normaloverflow-wrap只支持break-word。如果你的场景用了非标准的文字断行规则Pretext 的计算结果可能不准。macOS 上不建议用system-ui字体。因为 macOS 的系统字体在不同版本上可能映射到不同的实际字体Pretext 无法保证精度。建议指定具体的字体名称比如Inter或SF Pro。它不是一个完整的排版引擎。CSS Shapes 那种复杂的文字绕排不支持复杂的书写方向混排场景虽然做了处理支持 RTL 和双向文本但边缘 case 可能还有问题。简单说它的定位是精确测量和基础排版不是要取代 CSS而是补上 CSS 做不到的那一块。技术上是怎么实现的Pretext 的底层思路并不神秘但工程量很大。它利用 Canvas 的measureText()API 获取字体的字形数据。这个 API 虽然也是浏览器提供的但它不触发布局重排因为 Canvas 的文字测量是独立于 DOM 的。拿到字形数据之后Pretext 自己实现了断行算法。断行听起来简单实际上要处理的东西很多不同语言的断行规则不一样CJK 字符可以在任意位置断开英文单词不能从中间断Emoji 不能被拆散双向文本的行内排列顺序要反转。Pretext 的断行实现参考了 pdf.js 的双向文本处理以及 Sebastian MarkbageReact Fiber 架构设计者早期的一个文本布局项目。267 次提交89.5% 的代码是 TypeScript可以想象里面有多少边界情况要处理。换个角度看这件事Pretext 的出现本质上是在挑战一个延续了 30 年的默认假设文字排版是浏览器的事开发者不需要也不应该自己算。这个假设在 Web 1.0 时代完全成立。那时候网页就是文档浏览器负责排版天经地义。但今天的 Web 应用早就不是文档了。聊天界面、协作编辑器、数据可视化、AI 对话这些场景对文字排版的控制粒度要求远远超出了 CSS 的设计目标。有意思的是原生应用开发者一直都有文字测量的 API。iOS 的NSAttributedString.boundingRect、Android 的StaticLayout都可以在不实际渲染的情况下计算文字尺寸。Web 在这方面一直是缺失的Pretext 补上了这块拼图。当然它现在还是一个很早期的项目。API 可能会变边界情况可能还有 bug生产环境使用需要充分测试。但它证明了一件事把文字测量从 DOM 里拿出来这条路在技术上是可行的而且收益巨大。对前端工程师来说值得关注值得试试值得想想自己手上有没有被getBoundingClientRect卡住的场景。项目地址https://github.com/chenglou/pretext在线 Demochenglou.me/pretext/安装npm install chenglou/pretext

更多文章