1、字体基础与问题背景:为什么需要压缩?

大家好,我是这门课的主讲。咱们直接进入正题。

做前端或客户端开发的同学,十有八九都遇到过字体加载慢的问题。页面打开后,文字先消失几秒,然后突然蹦出来——这就是所谓的 FOIT(Flash of Invisible Text)。用户体验?说实话,挺糟糕的。

为什么会这样?说白了,字体文件太大了。一个完整的 TTF 或 OTF 字体,动不动就 5MB、10MB。你想想看,用户打开一个页面,先得下载 10MB 的字体文件,网络稍微差一点,页面就得白屏好几秒。这在移动端尤其致命。

我个人习惯,在项目初期就会把字体性能纳入考量。因为一旦上线后再去优化,改造成本会翻好几倍。

字体格式简介:TTF / OTF / WOFF

先聊聊字体格式。这是基础,但很多人其实没搞清楚它们的区别。

格式 全称 特点 典型场景
TTF TrueType Font Apple 和 Microsoft 联合开发,使用二次贝塞尔曲线。文件较大,未压缩。 桌面端、早期网页
OTF OpenType Font 基于 TTF 扩展,支持更多排版特性(连字、花体等)。使用三次贝塞尔曲线,压缩率略好。 专业排版、设计稿
WOFF Web Open Font Format 本质上是 TTF/OTF 加上 gzip 压缩。专为网页设计,包含元数据。 网页字体加载
WOFF2 WOFF 2.0 使用 Brotli 压缩算法,比 WOFF 再小 30%~50%。 现代网页字体加载
我的建议:新项目直接上 WOFF2。如果必须兼容老旧浏览器,备选 WOFF。TTF 和 OTF 只作为源文件保留,不要直接用于线上。

我记得有一次帮一个电商网站做性能优化,发现他们还在用 TTF 直接加载。一个字体文件 8MB,首页加载时间直接多了 3 秒。换成 WOFF2 后,文件降到 2.5MB,加载时间缩短了 60%。

网页中的字体性能瓶颈

网页字体加载,有几个绕不开的坑:

  • FOIT(Flash of Invisible Text):字体没加载完时,文字完全不可见。这是浏览器的默认行为。
  • FOUT(Flash of Unstyled Text):先用后备字体显示,字体加载完成后突然切换。视觉上会「闪」一下。
  • 阻塞渲染:CSS 中的 @font-face 声明如果放在关键路径上,会阻塞页面渲染。
  • 多字体叠加:一个页面用了 3~5 种字体,每个字体又包含多个字重,下载量直接爆炸。
我曾经踩过的坑:一个项目用了 4 种字体,每种字体包含 Regular、Bold、Italic、Bold Italic 四个文件。总共 16 个字体文件,加起来超过 40MB。页面在 3G 网络下加载了 12 秒才显示文字。后来我们做了子集化压缩,只保留用到的字符,文件总量降到 3MB 以内。

App 中的字体性能瓶颈

App 端的情况其实更复杂。你想想看,移动端网络不稳定,存储空间也有限。

  • 包体积膨胀:一个中文字体文件动辄 5~10MB,直接打进 APK/IPA 里,安装包体积瞬间变大。
  • 动态下载延迟:很多 App 采用按需下载字体的策略。但用户第一次看到特殊字体时,需要等待下载完成,体验很差。
  • 内存占用:字体加载到内存后,会常驻。如果同时加载多个字体,内存压力不小。
  • 渲染性能:某些字体在低端设备上渲染速度慢,会导致列表滚动卡顿。

我参与过的一个社交 App,用户发帖时可以用各种艺术字体。最初我们直接把 20 多种字体打包进安装包,结果安装包从 30MB 涨到 80MB。后来改成按需下载 + 字体子集化,安装包回到 35MB,用户下载字体的等待时间也从 5 秒降到了 1 秒以内。

核心逻辑:为什么需要压缩?

一句话总结:字体文件里大部分数据,你用不上。

一个完整的 TTF 字体,包含几千个字符。但你的页面或 App 可能只用到了其中的几十个。剩下的字符、排版规则、hinting 数据,都是冗余的。

压缩的核心思路就是:只保留你需要的部分,扔掉其余所有。

字体压缩核心逻辑 原始字体文件 冗余字符 + 数据 子集化 格式转换 压缩后字体文件 仅保留用到的字符 压缩前后对比 原始 5MB → 压缩后 200KB(仅保留常用中文字符 + 英文字母 + 数字) 识别用到的字符 提取字形数据 重新打包为 WOFF2

核心结论:字体压缩不是「有损压缩」,而是「精准裁剪」。你扔掉的是你不需要的数据,保留的是你需要的字形。所以叫「减容不减质」。

嗯,这里要注意一点:压缩后的字体文件,在渲染质量上跟原始字体完全一致。因为字形数据本身没有做任何有损处理,只是把没用的部分删掉了。这也是为什么这门课叫「减容不减质」——体积减小了,但显示效果一点不打折。