Files
codex-bot 2b3b3a1ae4
Verify and deploy Tabularium / verify-and-deploy (push) Successful in 5m47s
Expand document collections with Pretext tutorial and simplify library navigation
2026-10-28 13:42:51 +08:00

2.4 KiB
Raw Permalink Blame History

第二章:先看懂 prepare 与 layout 的分工

2.1 同一份输入,有不同的变化频率

聊天消息的文字和字体可能很久不变,但容器宽度会在拖动窗口时连续变化。把所有工作绑在一个函数里,每次 resize 都会重复做许多相同的事情。

Pretext 将它拆成两步:

import { prepare, layout } from '@chenglou/pretext'

const prepared = prepare('Hello 世界,今天一起读源码。', '16px Arial')

const narrow = layout(prepared, 200, 24)
const wide = layout(prepared, 400, 24)

这里 200、400 是内容宽度,24 是调用者指定的每行高度,单位都是 CSS 像素。

阶段 输入 主要工作 什么时候重做
prepare() 文字、字体、选项及语言环境 分析断行机会、测量、记录修正数据 相关输入变化
layout() prepared、宽度、行高 根据已知宽度决定换行并计数 容器宽度或行高变化

prepared 可以理解为“已经整理好的排版材料”。它与容器宽度无关,但并不包含任意宽度下现成的布局答案。

2.2 沿源码走一遍

打开 src/layout.ts,定位 prepareInternal():

读取引擎规则与语言
    → analyzeText(...)
    → measureAnalysis(...)
    → prepared handle

再定位 layout(),其核心逻辑是:

const lineCount = countPreparedLines(internal, normalizedWidth)
return { lineCount, height: lineCount * lineHeight }

这是省略类型与内部转换的源码摘义。它没有逐行调用 Canvas,也没有先创建 DOM 再读取高度。

2.3 一段文字被布局多次,省下多少工作

用 P 表示准备成本,用 L 表示一次布局成本,一共布局 k 次:

复用 prepared:P + k × L
每次重新 prepare:k × (P + L)

两种写法相差 (k - 1) × P。这解释了为什么使用方式会直接决定收益:在 resize 回调中重新 prepare,会让原本可以省掉的分析工作再次发生。

另外,重复 prepare 可能命中测量缓存,但仍要分析文本、组装新的 handle。它依然比直接复用已有 handle 做得更多。

本章检查:layout() 是 O(1) 吗?不是。它通常还要扫描分段。快来自减少每一步要做的工作,而不是完全不看文本。