自定义字体能让站点更有品牌感,但 MiSans、思源黑体这类中文字体完整包往往 4~8 MB。不做优化时,DevTools 里一条 font 请求就能占掉首屏大半时间。下面是一套可落地的优化路径。

1. 优先 woff2

@font-face {
font-family: 'MiSans';
src: url('/static/fonts/MiSans-Regular.woff2') format('woff2');
font-weight: 400;
font-style: normal;
font-display: swap;
}
  • 只提供 woff2,现代浏览器足够
  • font-display: swap 先用系统字体顶一下,避免长时间 invisible text

2. 按字重拆分文件

正文 400、标题 600/700 各一个文件。加粗样式异步加载:

<link rel="stylesheet" href="/static/fonts/fonts.css">
<link rel="stylesheet" href="/static/fonts/fonts-bold.css" media="print" onload="this.media='all'">

首屏只阻塞 Regular,Semibold 稍后补上,用户感知更顺。

3. 子集化:只留用到的字

fonttoolsglyphhanger 按页面字符集裁剪:

pip install fonttools brotli
pyftsubset MiSans-Regular.woff2 \
--text-file=chars.txt \
--output-file=MiSans-Regular.subset.woff2 \
--flavor=woff2

chars.txt 包含:常用汉字 + 站点导航 + 文章高频字。 完整包 4744 KB → 188 KB 这种量级很常见。

注意:博客持续发新文章时,要定期更新字集或保留稍大的常用字表(如 3500 常用字)。

4. preload 关键字体

<link rel="preload" href="/static/fonts/MiSans-Regular.woff2" as="font" type="font/woff2" crossorigin>

只 preload 首屏一定会用到的那一个 woff2,不要 preload 全部字重。

5. 避免 @import 链

/* ❌ 多一层阻塞 */
@import url('fonts.css');

/* ✅ 在 HTML 里直接 link */

@import 会串行解析,延迟字体请求起点。

6. 长缓存

字体文件带 hash 或版本号时,可设长期缓存:

Cache-Control: public, max-age=31536000, immutable

内容变了改文件名,而不是缩短缓存。

7. 测量方式

Chrome DevTools → Network 筛选 Font,看:

  • 下载体积(transferred)
  • 是否阻塞渲染
  • swap 后是否出现 FOUT(可接受)还是长时间 FOIT(需优化)

Lighthouse 的「Ensure text remains visible during webfont load」与此直接相关。

对照清单

手段作用
woff2体积最小
子集化体积骤降
按字重拆分减少首屏下载
preload提前握手
font-display: swap避免空白字
长缓存回访几乎零成本

字体优化是「一次性辛苦、长期受益」的工作。把 Regular 子集做到 200 KB 以内,再配合 preload,大多数博客站点的字体就不再是性能瓶颈了。