编辑
2026-04-11
公众号长文
0

目录

一、为什么这个库值得你花5分钟了解?
二、项目简介
三、核心功能深度解析
1. 零 DOM 测量——从根源解决性能问题
2. 手动行控制——Canvas/SVG 的排版神器
3. 光标导航——像 Textarea 一样精确
4. 服务端渲染——让 Node.js 也能计算文本布局
5. 多语言文本支持
四、9个 Demo 案例——从理论到实践
五、技术亮点与性能数据
核心原理:Canvas 文本测量 + 纯数学运算
性能对比(vs DOM 测量)
架构设计哲学
六、适用场景与人群
谁应该用?
谁不需要用?
七、快速上手
安装
基本用法
渲染到 Canvas
查看 Demo
八、总结
推荐阅读

一、为什么这个库值得你花5分钟了解?

做前端的人都知道这个场景——

写一个聊天消息列表,消息长度不一,有的长有的短。常见的做法是:先把消息渲染到 DOM 里,然后用 getBoundingClientRect() 或者 offsetHeight 去测量它有多高。

问题是:这些操作会触发浏览器强制重排(Reflow)!

一次两次还好,但如果你的列表有 100 条消息,每条消息都这样测量——浏览器可能要在同一帧里触发几十次重排,帧率直接从 60fps 掉到 10fps,用户就会感觉"卡"。

你可能会说:"那我加个 requestIdleCallback 延迟测量?" 但这治标不治本——测量结果不准确,布局还是会乱。

今天要介绍的这个库 Pretext,解决的正是这个底层问题: 在完全不触碰 DOM 的情况下,精确计算多行文本的高度和换行位置。

42.8K Stars,由前 Facebook 工程师 chenglou 打造。这个名字对前端社区来说并不陌生——他同时也是 react-virtualizeddata-ui 等知名项目的作者。

演示


二、项目简介

Pretext 是一个专注于"多行文本测量与排版"的 JavaScript/TypeScript 库,核心理念只有一句话:在真正渲染之前,预知文本的一切

它通过 Canvas 底层 API 完成文本测量,完全绕开了 DOM,因此不会触发任何重排(Reflow)或重绘(Repaint)。测量结果可以被缓存并重复使用。

关键指标数值
Stars42.8K
Forks2.3K
语言TypeScript / JavaScript
渲染目标DOM / Canvas / SVG / WebGL / 服务端
多语言支持CJK(中日韩)/ 阿拉伯语(RTL)/ 各类 Unicode
许可证MIT

一句话定位:不需要 DOM,就能知道文本长什么样的文本测量引擎


三、核心功能深度解析

1. 零 DOM 测量——从根源解决性能问题

传统方案的问题:

javascript
// ❌ 每次都触发 DOM 测量 + 重排 const el = document.createElement('div'); el.textContent = '这是一条很长的消息...'; document.body.appendChild(el); const height = el.offsetHeight; // 👈 强制重排!

Pretext 的做法:

javascript
import { prepare, layout } from '@chenglou/pretext'; // 第1步:一次性分析文本(不触碰 DOM!) const prepared = prepare('这是一条很长的消息,可能跨多行,需要精确知道每行的宽度...'); // 第2步:给定任意宽度,直接算出高度和换行位置 const result = layout(prepared, { width: 320 }); console.log(result.height); // 👈 精确的高度数值,不触发任何重排! // 如果宽度变了,只需要重新调用 layout() // prepared 数据可以被缓存复用 const result2 = layout(prepared, { width: 280 });

整个过程中,DOM 从未被触碰,没有 appendChild,没有 getBoundingClientRect,没有 offsetHeight

测量结果可以直接用于:

  • 虚拟列表的高度预计算
  • Masonry(瀑布流)布局的高度预测
  • 服务端渲染时的文本布局计算

2. 手动行控制——Canvas/SVG 的排版神器

当你需要在 Canvas 或 SVG 上渲染文本时,最大的难点在于:Canvas 没有内置的"自动换行"功能。

传统做法是:每行文字先渲染到临时 Canvas,测量宽度,超过边界就换行。效率低,而且很难处理混合样式(粗体、链接、代码片段)。

Pretext 提供了精确到字符的换行控制:

javascript
import { prepareWithSegments, layoutWithLines } from '@chenglou/pretext'; const segments = [ { text: '@深港数码', style: { color: 'blue' } }, // 蓝色用户名 { text: ' 推荐大家看看 ', style: {} }, // 普通文字 { text: 'Pretext', style: { bold: true } }, // 加粗关键词 { text: ' 这个库,真的很快!', style: {} }, ]; const prepared = prepareWithSegments(segments); const { lines } = layoutWithLines(prepared, { width: 280 }); lines.forEach((line, i) => { console.log(`第${i + 1}行: "${line.text}" 宽度=${line.width}`); // 👆 精确的每行文本内容,可以完美渲染到 Canvas/SVG 上 });

返回的 lines 数组包含了每行的:

  • 文本内容(text
  • 宽度(width
  • 字符范围(startIndex / endIndex
  • 每字符的 x 坐标

这意味着:任何复杂的富文本样式组合,你都能精确渲染到 Canvas 上,而且永远不需要用 DOM 打补丁。


3. 光标导航——像 Textarea 一样精确

javascript
import { walkLineRanges, measureLineStats } from '@chenglou/pretext'; const prepared = prepare(text); // 获取光标在第5行时的位置信息 const lineRanges = walkLineRanges(prepared, { width: 300 }); const charIndex = lineRanges[4].startIndex; // 第5行的起始字符索引 // 获取精确的行统计数据 const stats = measureLineStats(prelineStats, { width: 300 }); console.log(`第${5}行包含 ${stats.lineCount} 个字符`);

这套 API 可以用于:

  • 编辑器光标定位
  • 文本选择高亮
  • 代码编辑器的行号显示

4. 服务端渲染——让 Node.js 也能计算文本布局

javascript
// Node.js 环境下完全可用 import { prepare, layout } from '@chenglou/pretext'; const serverResult = layout( prepare('服务端渲染时也能精确知道文本高度'), { width: 375 } );

服务端预先计算好文本高度,前端 hydration 后直接使用,完全消除了布局抖动(Layout Shift)。这对于 SEO 和 Core Web Vitals(CLS 指标)有直接帮助。


5. 多语言文本支持

Pretext 内置了完善的 Unicode 处理逻辑,支持:

语言类型示例处理
中文/日文/韩文(CJK)你好世界等宽字符处理,无需空格分词
阿拉伯语(RTL)مرحبا右到左双向文本自动翻转
混合内容Hello 你好 مرحبا智能混合排版

对于东亚文字来说,最大的优势在于:不需要空格就能分词,传统 JS 文本库在这个场景下几乎无法工作,Pretext 处理得很好。


四、9个 Demo 案例——从理论到实践

项目提供了丰富的交互 Demo(访问 chenglou.me/pretext 查看):

Demo演示内容核心价值
Accordion手风琴展开/收起动画展开高度预知,动画丝滑
Bubbles聊天消息气泡,固定行数多行消息气泡宽度一致
Dynamic Layout障碍物感知的文字路径文字绕开障碍物流动
Variable Typographic等宽 vs 比例字体的精确对比展示 Pretext 的字符级精度
Editorial Engine杂志级多栏排版实时文本回流、侧边栏、图片穿插
Justification ComparisonCSS对齐 / 贪心断词 / Knuth-Plass 算法对比三种断行算法直观对比
Rich Text带代码、链接、Chips 的富文本混合样式精确换行
Markdown Chat虚拟化聊天列表虚拟列表 + Pretext 高度预计算
Masonry瀑布流卡片无 DOM 测量的瀑布流高度预测

五、技术亮点与性能数据

核心原理:Canvas 文本测量 + 纯数学运算

Pretext 的技术路径非常巧妙:

  1. 使用 Canvas measureText() API 获取字符级精确宽度(Canvas API 不触发重排)
  2. 缓存测量结果,所有后续计算都是纯 JavaScript 数字运算
  3. 实现 Knuth-Plass 排版算法(与 TeX 相同的断行算法),保证最优换行

这意味着:测量一次,后续无论调用多少次 layout(),都是纯算术运算,时间复杂度 O(1)

性能对比(vs DOM 测量)

操作DOM 测量Pretext
单次测量 100 字符~5ms~0.1ms
1000 条消息列表初始化触发多次 Reflow无 DOM 操作
宽度变化后重新计算需重新渲染 DOM直接调用 layout(),毫秒级
服务端计算无法使用✅ 完全支持

具体数据因浏览器和硬件差异而不同,但 Pretext 在所有场景下都显著优于 DOM 测量方案。

架构设计哲学

chenglou 在项目中贯彻了一个非常清晰的设计哲学:只做测量,不做渲染

Pretext 不试图成为一个完整的文本渲染引擎。它专注于:

  • 精确测量文本
  • 提供换行位置
  • 给出渲染所需的所有坐标数据

渲染部分完全交给用户——你可以渲染到 Canvas、SVG、WebGL,或者完全不用它,自己处理 DOM。

这种"专注单一职责"的设计让 Pretext 体积小、API 清晰、性能极高。


六、适用场景与人群

谁应该用?

场景使用方式
虚拟列表/虚拟滚动预计算列表项高度,无需先渲染再测量
聊天/消息界面多行消息气泡宽度精确一致,无跳动
瀑布流/Masonry 布局预知卡片高度,排列组合最优化
Canvas/SVG 文本渲染精确换行,支持混合样式
服务端文本预计算消除 CLS,提升 CWV 分数
富文本编辑器精确光标定位和行号计算
多语言国际化应用CJK / RTL 文本自动处理

谁不需要用?

  • 纯展示型静态页面,文本高度由 CSS 决定,不需要 JS 测量
  • 列表项高度固定,不需要动态计算
  • 你的项目里已经用了性能足够好的方案

七、快速上手

安装

bash
npm install @chenglou/pretext # 或 pnpm add @chenglou/pretext

基本用法

javascript
import { prepare, layout } from '@chenglou/pretext'; // 准备文本数据(一次性操作) const prepared = prepare('这是要测量的文本内容'); // 在任意宽度下计算高度 const { height, lines } = layout(prepared, { width: 320 }); console.log(height); // 精确的像素高度 console.log(lines); // 每行的详细信息数组

渲染到 Canvas

javascript
import { prepareWithSegments, layoutWithLines } from '@chenglou/pretext'; const segments = [ { text: 'Hello ', style: { color: 'black' } }, { text: 'Pretext', style: { bold: true, color: '#E8655A' } }, ]; const { lines } = layoutWithLines(prepareWithSegments(segments), { width: 200 }); // 渲染到 Canvas lines.forEach((line, i) => { ctx.fillText(line.text, 0, 20 + i * 24); });

查看 Demo

bash
git clone https://github.com/chenglou/pretext.git cd pretext bun install bun start # 打开 http://localhost:3000

八、总结

Pretext 是一个看起来"很小",但解决了一个"很底层"问题的库。

它的价值不在于功能多花哨,而在于:把文本测量这件事,做到了极致

42.8K Stars 说明了一切——这个库被大量项目依赖,涵盖了从聊天应用到高端编辑器,从虚拟列表到杂志排版的各种场景。

如果你正在做:

  • 任何涉及动态文本高度的 UI
  • 任何需要 Canvas/SVG 文本渲染的场景
  • 任何对性能有要求的前端应用

把 Pretext 加入你的技术栈,它会让这些问题变得异常简单。

项目链接:

推荐阅读

本文作者:KK

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!