惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

人人都是产品经理
人人都是产品经理
Stack Overflow Blog
Stack Overflow Blog
L
LINUX DO - 最新话题
Google Online Security Blog
Google Online Security Blog
Schneier on Security
Schneier on Security
Spread Privacy
Spread Privacy
www.infosecurity-magazine.com
www.infosecurity-magazine.com
雷峰网
雷峰网
Google DeepMind News
Google DeepMind News
Microsoft Azure Blog
Microsoft Azure Blog
IT之家
IT之家
V
Vulnerabilities – Threatpost
K
Kaspersky official blog
S
Schneier on Security
B
Blog
The Register - Security
The Register - Security
SecWiki News
SecWiki News
Hacker News: Ask HN
Hacker News: Ask HN
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
S
Security Affairs
T
The Blog of Author Tim Ferriss
G
Google Developers Blog
T
Tenable Blog
P
Proofpoint News Feed
Apple Machine Learning Research
Apple Machine Learning Research
D
DataBreaches.Net
S
Secure Thoughts
Security Latest
Security Latest
H
Heimdal Security Blog
The Hacker News
The Hacker News
O
OpenAI News
AWS News Blog
AWS News Blog
量子位
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
腾讯CDC
U
Unit 42
L
Lohrmann on Cybersecurity
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
L
LangChain Blog
阮一峰的网络日志
阮一峰的网络日志
T
The Exploit Database - CXSecurity.com
NISL@THU
NISL@THU
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
Application and Cybersecurity Blog
Application and Cybersecurity Blog
Hugging Face - Blog
Hugging Face - Blog
The Last Watchdog
The Last Watchdog
Recorded Future
Recorded Future
V2EX - 技术
V2EX - 技术
爱范儿
爱范儿
F
Full Disclosure

博客园 - 漫思

python使用.env构建开发和生产环境 python项目的构建 nodejs构建CICD时的思考  成为 AI 智能体工程师的 10 个步骤 es6的 yield python 中的 yield 笔记本的A壳 thinkpad 更换 reduce() in Python python的字段赋值和取值的操作 python的语法类似于lodash分组展开,合并分组操作 SelectMany C# lodash 数组的常用做法 lodash里面的常用方法 技术的边界 Reduce 和 Transduce 的含义 尤雨溪创办的 VoidZero 官宣加入 Cloudflare,前端 Vite 等保持开源 Ramda 函数库参考教程 ramda es 数组的方法 flatmap map object.entity的教程 Map与FlatMap:在数据处理中的区别与联系 flat、flatmap与map的用法区别1 flat、flatmap与map的用法区别 AI不是从天而降,它经历了七十年三起三落:读懂AI的第三课 Agent 17 种架构模式 分析 & 思考 只有踩过坑才懂:前端生成唯一 ID,别用 Date.now ()了!试试它crypto.randomUUID() FastAPI python并发 代码是 AI 写的,生产事故谁背锅? AI Agent 走出 Demo 幻觉的唯一解药:Harness Engineering 高管的 AI 精神病 小米开源可控视频音效生成模型 ControlFoley ​IBM 联合红帽投资 50 亿美元:帮助企业确保开源软件安全 国家大基金领投 DeepSeek,首轮融资投前估值 450 亿美元 SpaceX 自研 AI 训练栈 V1.0 接近完工:用 C 重写、适配 22 万块 GB300 GPU 高管的 AI 精神病 - 漫思 做一款企业真正敢用的AI测试应用,到底有多难?究竟难在哪? - 漫思 工良吐槽篇:万字长文细说 AI 落地之笑谈 koarouter的路由基本能力 7. 简单语句 class-validator class- @koa/router 分路由 这是我见过的最好的koa2 classvalidate typeorm的能力 HLS.js destroy时会取消请求吗 从入门到精通:2025年本体管理工具选型指南,8款工具助你驾驭语义网络 一文入门智能体:dify 超快速构建AI agent C# 15 类型系统改进:Union Types skill网站 应无所住,而生其心 process.stdin.isTTY 谷歌学术走过风雨十年 听创始人畅谈苦辣酸甜 - 漫思 process.stdin.isTTY 是false的具体情况 asyncio 较为复杂的demo asyncio 简单demo python Annotated 独家解读:淘宝使用 Node.js 的 TypeScript 多场景开发和实践 mac uv 使用 Python 包管理工具 uv 使用教程 Python依赖管理新标杆:UV工具安装与实战指南 python的uv lobehubui python的enum通过int进行初始化 python的枚举类型 深度拆解:AI 智能体 Harness 的构造【译】 LlamaIndex是什么?LlamaIndex综述!看这一篇就够了! python和langchain的简单教程 nodejs的顶尖开源项目 基础Pydantic+TypedDict+Annotated +Dataclass 深度解析Python结构化数据工具:dataclass、Pydantic Model与TypedDict 被 LangChain 全家桶搞晕了?LangGraph、LangSmith、LangFlow 一文读懂 - 漫思 谁才是企业级开源平台的优选?OpenCSG与Dify、Coze、Langflow、Ollama 的差异化之路 四大框架全景解析:LangChain、LangGraph、DeepAgent、LangFlow 测试数据管理方式对比:dict vs dataclass vs TypedDict 革命性的写作:MDX 让你的 Markdown 全面动起来 Python - pydantic 入门介绍与 Models 的简单使用 欢迎使用 Pydantic¶ python模块导入 Python 里通常说“数组切片”,大多数时候指的是列表切片。基本格式是: 自定义hook的写法 Python 遍历字典的8种方法 python基础教程 or的简单用法 C# 15 引入了 联合类型 asyncio的基础理论 flask配置热更新 Form.Item 无法直接绑定两个 name Form.Item 的双向翻译官:getValueFromEvent 与 getValueProps Knex.js入门指南:Node.js最强大的SQL查询构建器 knex.js1 Knex.js RFC 9535:JSONPath 的标准化之路 C#多线程Thread.Join()的详解 Flask入门(四):Flask静态文件及配置 【高并发】消息队列思路 为何高并发系统中都要使用消息队列?这次彻底懂了! Node.js 消息队列应用:RabbitMQ、Kafka 与处理高并发任务 大文件上传下载处理方案-断点续传,秒传,分片,合并 Node.js 缓存策略优化:Redis、Memcached 与本地缓存实现 .NET开发者的救星:8个让你告别996的高效库 别再吹牛了,100% Vibe Coding 存在无法自洽的逻辑漏洞!
Pretext:值得关注的文本排版引擎
漫思 · 2026-04-09 · via 博客园 - 漫思

Pretext 是一个用 TypeScript 实现的用于多行文本精确测量和布局的引擎。不碰 DOM,不触发 reflow,却能完美匹配浏览器字体引擎在各种语言、emoji、混合文字方向下的真实表现。它刚刚发布,发展势头很猛。

我觉得这是过去十年里最值得关注的文本引擎之一。它不是小打小闹的优化,而是把文本这块一直卡着大家脖子的核心问题彻底解决掉了。

Cheng_Lou_-_3._Multi-columns_magazine_layout_but_responsive_and_dynamic_1TFW32

先说清楚它解决什么痛点。网页上但凡涉及动态文本,比如聊天消息、文章卡片、虚拟列表、自动换行输入框、响应式排版,你都得问浏览器“这个文本在多宽容器里到底占多少高”。过去唯一办法是扔进 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)  

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)  

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
  

这些 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 直接把这些脏活全包了。你只需要专注业务逻辑。

不止性能

  1. 虚拟化列表。以前 variable height 虚拟列表要么估高、要么 DOM 测量后强制 rerender,滚动时容易闪。现在 Pretext 提前算出所有高度,occlusion culling 变成线性扫描,Masonry 那种不规则网格也能 120fps。演示里几十万文本框滚动丝滑,就是证明。

  2. 布局偏移(CLS)彻底消失。新文本加载时,不再需要等 DOM 渲染再调整锚点。高度已知,scroll position 直接算。

  3. Canvas / SVG / WebGL 渲染一致性。以前 DOM 文字和 Canvas 文字永远对不齐,现在同一份 prepared 数据,两边渲染结果像素级一致。游戏 UI、数据可视化、设计工具直接起飞。

  4. 响应式高级排版。聊天气泡自动 shrinkwrap、最优宽度二分搜索、杂志式多栏动态流式、文字绕图、variable font ASCII art……以前 CSS 做这些要一堆 hack,现在一行 layout 调用搞定。演示里的动态布局 demo,像印出来的杂志,却能实时拖拽窗口自适应。

  5. 编辑器与 AI 内容生成。实时流式输出时,不再卡在测量循环。drop cap、justification(Knuth-Plass 算法已有人基于 Pretext 实现)、自动平衡文本、widows/orphans 处理,全都可编程。社区已经有人做爆炸文字、倾斜阅读、3D 排版、手写动画流式渲染,创意直接爆炸。

  6. 开发体验与 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。文本不再是瓶颈,而是创意的起点。

参考资料