字体加载策略:@font-face 规则详解、字体格式声明、字体子集化
好,咱们直接进入正题。字体加载这件事,说简单也简单,说复杂……嗯,坑确实不少。我刚开始做前端那会儿,觉得用个自定义字体嘛,不就是写个 @font-face 就完事了?结果线上页面加载慢得像蜗牛,字体文件动不动就几 MB,用户等得直骂娘。
这一章,咱们就把 @font-face 的底裤扒干净。从规则怎么写、格式怎么选,到怎么给字体“瘦身”,一条龙讲透。
2.1 @font-face 规则详解
@font-face 是 CSS 里用来声明自定义字体的规则。说白了,就是告诉浏览器:“嘿,我这里有个字体文件,你拿去用吧。”
最基本的写法长这样:
@font-face {
font-family: 'MyCustomFont';
src: url('fonts/MyCustomFont.woff2') format('woff2');
font-weight: normal;
font-style: normal;
font-display: swap;
}
这里有几个关键属性,我挨个说一下。
2.1.1 font-family:给字体起个名
这个值就是你给字体取的名字。后面在 font-family 属性里引用它就行。注意,名字可以随便起,但别跟系统字体重名,否则会覆盖系统字体,引发奇怪的问题。
2.1.2 src:告诉浏览器字体文件在哪
src 属性可以写多个值,浏览器会按顺序尝试加载。我个人习惯把最优的格式写在最前面,比如 woff2 优先,然后是 woff,最后是 truetype 或 opentype。
@font-face {
font-family: 'MyFont';
src: url('MyFont.woff2') format('woff2'),
url('MyFont.woff') format('woff'),
url('MyFont.ttf') format('truetype');
}
你想想看,浏览器拿到这个规则后,会先问自己:“我支持 woff2 吗?” 支持就用,不支持就往下看 woff,再不行就用 ttf。这叫“优雅降级”。
2.1.3 font-weight 和 font-style:匹配字重和样式
这两个属性用来声明这个字体文件对应的是哪个字重(粗细)和样式(斜体/正体)。
举个例子,如果你有四个字体文件:
MyFont-Regular.woff2— 字重 400,正常样式MyFont-Bold.woff2— 字重 700,正常样式MyFont-Italic.woff2— 字重 400,斜体样式MyFont-BoldItalic.woff2— 字重 700,斜体样式
那你就得写四个 @font-face 块:
@font-face {
font-family: 'MyFont';
src: url('MyFont-Regular.woff2') format('woff2');
font-weight: 400;
font-style: normal;
}
@font-face {
font-family: 'MyFont';
src: url('MyFont-Bold.woff2') format('woff2');
font-weight: 700;
font-style: normal;
}
@font-face {
font-family: 'MyFont';
src: url('MyFont-Italic.woff2') format('woff2');
font-weight: 400;
font-style: italic;
}
@font-face {
font-family: 'MyFont';
src: url('MyFont-BoldItalic.woff2') format('woff2');
font-weight: 700;
font-style: italic;
}
这样,当你在 CSS 里写 font-weight: bold 或 font-style: italic 时,浏览器就能找到对应的字体文件。
font-weight: 100 900; 这种范围写法。不过,实际效果取决于字体文件本身是否包含多字重数据。大部分情况下,还是建议每个字重单独一个文件。
2.2 字体格式声明
字体格式这事儿,说白了就是浏览器怎么“读懂”你的字体文件。不同的格式,压缩率、兼容性都不一样。
目前主流的字体格式有这几种:
| 格式 | 扩展名 | 压缩率 | 浏览器支持 | 我的建议 |
|---|---|---|---|---|
| WOFF2 | .woff2 | 最高(比 WOFF 小 30%~50%) | 现代浏览器全支持 | 首选,能上就上 |
| WOFF | .woff | 较高 | 几乎所有浏览器 | 备选,兼容旧浏览器 |
| TrueType / OpenType | .ttf / .otf | 无压缩或少量压缩 | 广泛支持 | 兜底方案,文件较大 |
| EOT | .eot | 较低 | 仅 IE 老版本 | 如果你还在支持 IE8/9,可以考虑 |
| SVG 字体 | .svg | 低 | 仅旧版 Safari / iOS | 现在基本不用了 |
我个人习惯的声明顺序是:woff2 → woff → ttf。这样现代浏览器能拿到最小的文件,老浏览器也能兜底。
2.3 字体子集化
字体子集化,说白了就是“瘦身”。一个完整的字体文件可能包含几千个字符,但你的页面可能只用到了其中的几十个。比如你只用了英文和数字,却加载了一个包含中文、日文、韩文、拉丁文扩展区的字体文件,那大部分流量都浪费了。
子集化就是只保留你需要的字符,把不需要的统统删掉。这样文件体积能缩小 90% 以上。
2.3.1 怎么做子集化?
常用的工具有 glyphhanger、fonttools、subfont 等。我拿 glyphhanger 举个例子:
# 先分析页面用到了哪些字符
glyphhanger https://example.com
# 然后生成子集字体
glyphhanger --subset=MyFont.ttf --US_ASCII --formats=woff2,woff
这个命令会扫描页面,提取所有用到的字符,然后生成只包含这些字符的 WOFF2 和 WOFF 文件。
如果你不想用命令行,也可以在线工具,比如 Font Squirrel 的 Webfont Generator,或者 Google Fonts 的 ?text= 参数。
2.3.2 子集化的策略
我一般按这几种情况来:
- 只包含拉丁字符: 用
--US_ASCII或--latin参数,文件能小到 2~5 KB。 - 包含常用标点: 加上
--latin-ext,文件大概 5~10 KB。 - 中文场景: 中文子集化比较麻烦,因为常用字就有 3500 个左右。我建议只保留页面实际出现的汉字,或者按“常用 1000 字”来切。
2.3.3 子集化后的 @font-face 写法
子集化后的字体文件,@font-face 写法完全一样。只是文件体积小了很多。
@font-face {
font-family: 'MySubsetFont';
src: url('MySubsetFont.woff2') format('woff2'),
url('MySubsetFont.woff') format('woff');
font-weight: 400;
font-style: normal;
font-display: swap;
}
你看,代码没变,但加载速度天差地别。
2.4 本章知识体系图
下面这张图,把 @font-face 规则、字体格式、子集化之间的关系串起来了。你可以把它当作本章的“脑图”。
2.5 实战建议
好了,理论讲完了,咱们来点实际的。我总结了几条经验,你直接拿去用:
- 能上 WOFF2 就上 WOFF2。 别犹豫,这是目前最好的格式。
- 子集化是必选项,不是可选项。 哪怕你的字体只包含 26 个英文字母,子集化后也能小一半。
- @font-face 声明要完整。
font-weight、font-style、font-display一个都不能少。 - 多个字重就写多个 @font-face 块。 别偷懒写在一个块里,浏览器会懵的。
- 构建阶段自动化子集化。 用 Webpack 插件或 Gulp 任务,别手动搞。
&text=Hello 参数,只加载你需要的字符。比如 https://fonts.googleapis.com/css2?family=Roboto&text=Hello。这样加载的字体文件只有几 KB。嗯,Google 早就帮你想好了。
这一章的内容就到这里。记住,字体加载的优化,核心就是两件事:选对格式 和 只加载需要的字符。把这两点做好,你的页面加载速度至少能快 50%。