Files
endorphin-tabularium-docs/content/docs/pretext/04-chapter.md
T
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

57 lines
2.9 KiB
Markdown

# 第四章:测量缓存如何减少浏览器工作
### 4.1 Canvas 提供的是字体测量能力
浏览器已经知道如何选择字体、塑造字形、计算宽度。Pretext 通过:
```js
context.font = '16px Arial'
const width = context.measureText('hello').width
```
获得字体事实。它优先创建 `OffscreenCanvas`;没有该能力时,创建不挂载到文档的普通 Canvas。
`OffscreenCanvas` 这个名字不表示测量会自动进入另一个线程。代码在哪里调用,工作就在哪里执行;想在 worker 中使用,需要应用自己安排,还有第八章的准确性限制。
### 4.2 两层复用各解决什么问题
第一层是应用保留 prepared。第二层在库内部,以字体等测量条件组织缓存,再按分段文本查找:
```text
字体 A 的缓存
'hello' → 测量结果
'world' → 测量结果
字体 B 的缓存
'hello' → 另一份测量结果
```
在 [`measurement.ts`](https://github.com/chenglou/pretext/blob/0d102a5cd933611e86a7e60bda7b4baa5995c6b0/src/measurement.ts) 搜索 `getSegmentMetrics()`,会看到这一核心表达式:
```js
measurement.metrics.get(seg) ?? addMetrics(...)
```
命中就读取结果;未命中才调用 Canvas 并存入缓存。相同词语出现在不同消息里,也有机会复用,不要求整段消息完全相同。
实际缓存不只有一个数字。`SegmentMetrics` 还会保留拆词适配、emoji 统计或其他修正所需的信息。语言变化和字距造成的 shaping 模式变化也受到专门处理;不能把它简单描述成全局 `(text, font)` 永久不变的表。
### 4.3 命中缓存后,连字体设置都尽量省
`getFontMeasurement()` 先找到字体对应的数据;真正测量时才通过 `getContext()` 设置 Canvas 的字体和测量模式。
为什么把两者拆开?富文本可能反复查询许多 item 的字体,但这些 item 的文字全都命中缓存。既然无需测量,也就不必为了查询而触碰 Canvas。
源码同时照顾了字体后加载等浏览器行为,因此不是无条件地“字体字符串相同就永不赋值”。读这一段时应一起看注释,而不要只把它当成普通 setter 缓存。
### 4.4 prepared 与全局测量缓存的寿命不同
全局缓存为了复用测量,prepared 为了一段具体文本的后续布局。二者不应混为一谈。
`MAX_CACHED_SEGMENTS = 1 << 18`,即 262,144。所有字体缓存的分段数超过边界后,下一次准备开始时会清理测量缓存。它不是每次插入都逐出一个条目的 LRU,因此单次准备期间可能超过阈值。
已经创建的 handle 保留自己的布局数据,不会因为这次缓存清理突然失效。不过,如果实际字体或语言变了,旧 handle 也不会自动更新,仍需要重新准备。
本章检查:清理全局缓存会自动把旧 prepared 换成新字体宽度吗?不会。缓存清理和重新创建 handle 是两步。