1. 字体渲染原理:从文件格式到屏幕像素
做前端性能优化,绕不开字体。你想想看,一个页面加载了 3 秒还没显示文字,用户早就跑了。我最早接触字体优化是在一个电商大促项目里——页面首屏有个特殊标题字体,结果白屏时间直接多了 1.2 秒。从那以后,我就开始认真研究字体到底是怎么工作的。
这一章我们先打好基础。搞清楚字体文件格式、渲染管线、回退机制这三个核心概念,后面的优化手段才有落脚点。
1.1 字体文件格式:TTF / OTF / WOFF 到底差在哪?
先说格式。你平时可能见过 .ttf、.otf、.woff、.woff2 这些后缀。它们不是随便起的,背后有各自的压缩算法和适用场景。
| 格式 | 全称 | 压缩率 | 浏览器支持 | 我的建议 |
|---|---|---|---|---|
| TTF | TrueType | 无压缩 | 所有浏览器 | 仅用于本地开发 |
| OTF | OpenType | 无压缩 | 所有浏览器 | 适合设计软件,不适合 Web |
| WOFF | Web Open Font Format | gzip 压缩(约 40%) | IE9+、现代浏览器 | 生产环境可用 |
| WOFF2 | Web Open Font Format 2 | Brotli 压缩(约 50-70%) | Chrome 36+、Firefox 39+、Safari 12+ | 首选格式 |
核心结论:WOFF2 是目前 Web 字体性能最优的选择。我在项目中做过实测,一个 300KB 的 TTF 字体,转成 WOFF2 后只剩 90KB 左右。加载时间从 1.8 秒降到 0.6 秒。
你可能会问:为什么不用 WOFF2 一统天下?因为老浏览器不支持。我建议的部署策略是:WOFF2 为主,WOFF 兜底,TTF 作为最后回退。后面讲 @font-face 声明顺序时,我会详细说怎么配。
1.2 字体渲染管线:从字节到像素的完整旅程
字体文件下载到浏览器后,不是直接就能显示文字的。它要经过一条完整的渲染管线。我画了一张图,帮你理清这个流程。
我来拆解一下每个阶段具体做了什么:
- 字体文件下载:浏览器通过
@font-face声明的 URL 去下载字体文件。这个阶段最耗时间,也是我们优化的主战场。 - 字节流解析:浏览器把下载到的二进制数据解析成内部数据结构。TTF 和 OTF 用的是不同的表结构,解析方式也不同。
- 字形轮廓提取:从字体文件中提取出每个字符的轮廓信息。TrueType 用二次贝塞尔曲线,OpenType 用三次贝塞尔曲线,精度更高。
- 栅格化:把矢量轮廓转换成像素点阵。这一步很吃 CPU,尤其是字号大、字符多的时候。
- 像素渲染:把栅格化后的位图绘制到屏幕上,还要考虑抗锯齿、次像素渲染等效果。
一个小技巧:我在做性能分析时,经常用 Chrome DevTools 的 Performance 面板,勾选「Raster」和「Main Thread」来观察字体栅格化的耗时。如果发现某个字体栅格化时间超过 50ms,就该考虑换格式或者做子集化了。
1.3 字体回退机制:当字体加载失败时
字体加载不是 100% 成功的。网络断了、CDN 挂了、用户浏览器不支持——这些情况我都遇到过。所以必须设计好回退策略。
浏览器处理字体回退的规则其实很简单:
- 按
@font-face中声明的src顺序依次尝试加载 - 如果第一个格式加载失败,尝试下一个
- 如果所有自定义字体都失败,使用
font-family中指定的系统字体
举个例子,这是我在生产环境中常用的声明方式:
@font-face {
font-family: 'MyCustomFont';
src: url('myfont.woff2') format('woff2'),
url('myfont.woff') format('woff'),
url('myfont.ttf') format('truetype');
font-display: swap;
}
body {
font-family: 'MyCustomFont', 'PingFang SC', 'Microsoft YaHei', sans-serif;
}
我曾经踩过的坑:有一次我把 TTF 放在了 WOFF2 前面,结果浏览器优先下载了更大的 TTF 文件,导致页面加载变慢。后来我才意识到,src 的顺序就是浏览器的尝试顺序——一定要把 WOFF2 放在最前面。
关于 font-display: swap,这里多说一句。它控制的是字体加载期间的显示行为:
| font-display 值 | 行为描述 | 适用场景 |
|---|---|---|
| auto | 浏览器默认行为(通常等同 block) | 不推荐,不可控 |
| block | 最多等待 3 秒,期间隐藏文字(FOIT) | 品牌字体,必须显示 |
| swap | 立即用回退字体显示,加载完成后替换 | 推荐,兼顾体验与性能 |
| fallback | 短时间等待(约 100ms),然后回退 | 字体加载较快时使用 |
| optional | 极短等待,如果没加载完就用回退 | 非关键字体,允许不加载 |
我的个人习惯:对于正文内容,我几乎只用 swap。用户不会因为字体变了而离开,但会因为白屏而离开。对于标题或品牌字体,我会用 block 配合预加载策略,确保字体尽快到位。
1.4 本章小结
这一章我们聊了三件事:
- 字体格式:WOFF2 是首选,TTF 只用于开发
- 渲染管线:下载 → 解析 → 轮廓提取 → 栅格化 → 像素渲染,每个环节都有优化空间
- 回退机制:用
font-display: swap+ 合理的src顺序,保证用户始终能看到文字