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 字体渲染管线:从字节到像素的完整旅程

字体文件下载到浏览器后,不是直接就能显示文字的。它要经过一条完整的渲染管线。我画了一张图,帮你理清这个流程。

字体渲染管线流程图 字体渲染管线 字体文件下载 字节流解析 字形轮廓提取 栅格化 像素渲染 关键瓶颈 网络传输耗时 解析 + 栅格化 CPU 开销 优化切入点 WOFF2 压缩 字体子集化

我来拆解一下每个阶段具体做了什么:

  1. 字体文件下载:浏览器通过 @font-face 声明的 URL 去下载字体文件。这个阶段最耗时间,也是我们优化的主战场。
  2. 字节流解析:浏览器把下载到的二进制数据解析成内部数据结构。TTF 和 OTF 用的是不同的表结构,解析方式也不同。
  3. 字形轮廓提取:从字体文件中提取出每个字符的轮廓信息。TrueType 用二次贝塞尔曲线,OpenType 用三次贝塞尔曲线,精度更高。
  4. 栅格化:把矢量轮廓转换成像素点阵。这一步很吃 CPU,尤其是字号大、字符多的时候。
  5. 像素渲染:把栅格化后的位图绘制到屏幕上,还要考虑抗锯齿、次像素渲染等效果。

一个小技巧:我在做性能分析时,经常用 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 顺序,保证用户始终能看到文字