npm install @chenglou/pretext

无需触碰 DOM 的文本测量库

Pretext 是一个纯 TypeScript 文本排版库,绕过浏览器的 layout reflow,用纯数学计算文本高度和布局。速度比 DOM 测量快 300-600 倍,支持多语言、emoji、双向文本,专为 Canvas / SVG / WebGL 时代而生。

文本排版和测量,一直是解锁更有趣 UI 的最后一个大瓶颈——在 AI 时代尤其如此。

— Cheng Lou,Pretext 作者
10k+
GitHub Stars
0.0002ms
layout() 单次耗时
300-600x
快于 DOM 测量
5KB
gzip 后体积

安装与使用

$ npm install @chenglou/pretext
基本用法TypeScript
import { prepare, layout } from '@chenglou/pretext';
 
// 第一步:一次性分析字体(耗时 1-5ms,只跑一次)
const prepared = await prepare('Hello, 世界 🌍', '16px Inter');
 
// 第二步:按容器宽度计算高度(耗时 0.0002ms,随时调用)
const { height, lineCount } = layout(prepared, 300);
 
console.log(height);     // 精确的像素高度
console.log(lineCount);  // 行数

Pretext 同时支持 ESM 和 CJS,可在浏览器、Node.js 和 Web Worker 中使用。

社区创意 Showcase

X / Twitter 上病毒式传播的社区作品,展示 Pretext 解锁的新 UI 可能性

Web 又变得有趣起来了

@EsotericCofe

"Web 终于又变得有趣起来了。"

发光小龙玩得很开心

@Riyvir

"这只发光小龙因为 Pretext 高兴坏了。"

人生中最神奇的时刻

@reathchris

"我正活在人生中最神奇的时刻。"

终于能发布我梦想中的文本排版

@rauchg

"我终于能把梦想中的文本排版做出来了。"

流体界面太强了

@vamsibatchuk

"这太绝了,流体界面万岁。"

你的物理课本再也不无聊了

@stevibe

"你的物理课本再也不无聊了。"

离谱的水波纹效果

@dushankas

"太离谱了。"

来玩 Pretext Breaker

@singular_prism

"来吧,玩玩 Pretext Breaker 🎮"

为什么需要 Pretext?

每次调用 getBoundingClientRect() 或 offsetHeight,浏览器必须暂停所有 JavaScript 执行,重新计算整个页面的几何结构,才能返回一个数字。这是前端性能中最昂贵的操作之一。对于包含 500 个文本块的页面,DOM 测量需要 15-30ms 并触发 500 次 layout reflow;而 Pretext 完成同样的任务只需 0.05ms,零 reflow。

传统方式

创建不可见 DOM 元素 → 设置 CSS 属性 → 插入文档 → 调用 getBoundingClientRect() → 删除元素。每次测量都强制触发完整的页面 layout reflow,500 个文本块就是 500 次 reflow。

Pretext 方式

prepare() 一次性用 Canvas measureText API 分析字体数据并缓存,layout() 之后全部是纯算术计算。不触碰 DOM,不触发 reflow,可在任何时机调用——包括 Web Worker 和服务端。

虚拟滚动

用 Pretext 可在渲染前精确预知每条消息高度,无需估算或在渲染后二次测量,实现零误差的虚拟化列表

防止 Layout Shift

SSR 阶段提前用 Pretext 计算布局,内容加载时不发生抖动,CLS 指标归零

Canvas / WebGL 渲染

Pretext 让文本脱离 DOM 也能精确排版,支持创意视觉效果、60fps 文字动画和高性能游戏场景

Masonry 瀑布流

不依赖 DOM 测量即可计算卡片高度,Pretext 将布局计算提前到数据加载阶段

障碍物感知排版

借助 Pretext,文字可实时绕任意形状流动,80 段动画生物在页面中移动时文字持续 60fps 重排

SVG / 服务端渲染

配合 opentype.js 将文本转为 SVG 路径,服务端生成零字体依赖的矢量排版

工作原理

Pretext 采用两阶段设计,将昂贵的测量与廉价的计算彻底分离

01

prepare() — 一次性字体分析

Pretext 首先对文本进行 CSS white-space 规范化,再用浏览器内置的 Intl.Segmenter 进行 Unicode 分词——正确处理 CJK 逐字换行、无空格泰语、阿拉伯 RTL 脚本。分词后,Canvas measureText API 测量每个字符段的宽度并缓存。整个 prepare() 阶段耗时 1-5ms,只执行一次。

02

layout() — 纯算术计算

有了缓存的字体度量数据,每次 Pretext 的 layout() 调用都只是纯数学运算:遍历缓存的字符段宽度,按 maxWidth 决定换行位置,计算出行数和总高度。单次耗时约 0.0002ms,可在每个动画帧中高频调用而不影响性能。

03

Safari emoji 校准

Safari 的 Canvas 报告的 emoji 宽度与实际 DOM 渲染存在差异。Pretext 在每种字体首次遇到 emoji 时,执行一次隐藏 DOM 测量来建立校正系数并缓存——此后所有 emoji 测量都使用这个校正值,确保跨浏览器像素级精度。

04

验证机制

Pretext 内置验证逻辑:在调试模式下,可将纯算术计算结果与实际 DOM 渲染高度进行并排对比,确保测量结果与浏览器渲染严格一致。这也是 Pretext 的准确性在代码中有据可查的来源。

支持的文本类型

CJK 中日韩混排

Pretext 对中文、日文、韩文支持逐字换行,与拉丁文混排时正确处理边界

Emoji 完整支持

Unicode emoji 序列、肤色修饰符、ZWJ 序列,跨浏览器精确测量

双向文本 BiDi

Pretext 可正确处理 RTL 阿拉伯文、希伯来文与 LTR 文字混排时的换行和宽度计算

无空格语言

泰语等无词间空格的语言,依赖 Intl.Segmenter 正确分词

软连字符

支持 ­ 软连字符的智能断词,段与段之间应用正确的字距规则

富文本内联元素

Pretext 支持内联代码块、链接、标签芯片等非文本元素与文字的混排计算

与其他方案的对比

Pretext 不是唯一的文本测量方案,但在速度、精度、语言支持的综合表现上最为均衡

DOM 测量

触发 layout reflow,500 个文本块需 15-30ms

Canvas.measureText 原始调用

需自行实现换行算法、语言分词、emoji 校准

HarfBuzz (WASM)

专业字体引擎,但 WASM 包体较大,部署成本高

Pretext

0.0002ms/次,5KB gzip,Intl.Segmenter + Canvas 组合方案

关于作者

Cheng Lou

Cheng Lou

@chenglou

前 React 核心团队成员,ReasonML(现 Melange)联合创建者,现任 Midjourney 前端工程师。Pretext 的开发过程本身也颇具意思:他向 Claude Code 和 Codex 等 AI 编程助手提供浏览器渲染基准,让 AI 在不同关键容器宽度下反复测试并优化实现,历时数周。在 Midjourney 内部验证了 Pretext 的工业级可靠性后,将其开源。

社区反响

2026 年 3 月发布后,在前端社区迅速引发讨论

文本排版和测量,一直是解锁更有趣 UI 的最后一个大瓶颈——在 AI 时代尤其如此。

Pretext 解决了长期以来非 DOM 环境中文本高度计算无解的难题。

这不是炫技 demo,而是替换 CSS 基础设施的底层工具——UI 范式的真正转移。

1600 万
发布推文阅读量
10k+
一周内 GitHub Stars
数十家
著名媒体报道

已知限制

Pretext 不是银弹,使用前应了解这些边界条件

依赖 Canvas API

Pretext 需要 Canvas 支持,无法在纯文字终端或无 Canvas 的渲染环境中运行。现代浏览器均支持,实际使用中通常不是问题。

字体必须预先加载

prepare() 调用时字体必须已完全加载。若使用 Web Font 且字体异步加载,需确保 font-display: block 或在 font.load() Promise resolve 后再调用 Pretext。

prepare() 有初始开销

首次调用 Pretext 的 prepare() 耗时 1-5ms,超大文本块可能更长。建议在数据加载阶段异步执行,不要放在渲染关键路径上。

覆盖范围:standard CSS 换行规则

Pretext 当前目标是 white-space: normal + word-break: normal + overflow-wrap: break-word + line-break: auto,暂不覆盖 pre-wrap 或其他非标准换行模式。