在线工具集

Web 字体加载完全指南:FOIT、FOUT、可变字体与 Google Fonts vs 自托管

Web 字体是 2026 年页面性能与设计感之间最大的张力点之一。一个精心选择的字体能让品牌传达更精准、阅读更舒服;但一个字体文件动辄数百 KB 到数 MB,加载策略错了就会让 LCP 飙升、文字闪烁、用户在白屏前发懵。本文系统讲解 Web 字体加载的全套技术:@font-face 基础、font-display 的五种策略(auto / swap / fallback / optional / block)、rel=preload 提示、可变字体(Variable Fonts)的节省效果、子集化(subsetting)原理、Google Fonts 与自托管在中国大陆的延迟差异、关键字体注入策略、字体回退栈设计。读完你能为自己的项目设计出既快又好看的字体加载方案。

1. @font-face 与字体声明基础

@font-face 是 CSS 中声明自定义字体的入口。一个完整声明包括 font-family(命名)、src(资源 URL,多个备选)、font-weight、font-style、font-display、unicode-range(字符范围限定)等。

2026 年的最佳实践 src:只用 woff2 格式。woff2 比 woff 压缩率高 30%,比 ttf 高 50%+,浏览器支持已超 99%。可以省略其他格式(不再需要 ttf 后备)。

示例:@font-face { font-family: "Inter"; src: url('/fonts/inter-var.woff2') format('woff2-variations'); font-weight: 100 900; font-display: swap; }

关键点:font-weight 用范围(100 900)声明可变字体,让 CSS 中所有 font-weight 值(如 font-weight: 450)都能命中同一个文件。

2. FOIT vs FOUT:闪烁的两种形式

当浏览器遇到 @font-face 声明的字体但还没下载完时,文字如何渲染?两种行为:

- FOIT(Flash of Invisible Text):文字保持隐形(透明 / 占位空白),直到字体加载完成才显示。优点是不会切换字体;缺点是网络慢时用户长时间看到「白板」,特别移动端 4G 网络可能持续 1-2 秒以上,对 FCP 与 LCP 都是灾难。

- FOUT(Flash of Unstyled Text):先用后备字体(如 Arial)显示文字,自定义字体加载完成后切换。优点是内容立即可读,FCP 极佳;缺点是字体切换瞬间会有视觉跳动(特别是字宽差异大时还会布局抖动)。

2026 年共识:除了 logo 等强字体依赖场景,主体文字一律选 FOUT。font-display: swap 是默认推荐。

3. font-display 五种策略详解

font-display 控制字体加载期 + 切换期的渲染行为,五种值:

- auto:交给浏览器,多数浏览器实际是 FOIT 行为。不推荐显式使用。

- block:阻塞 3 秒,阻塞期间文字不可见(FOIT),3 秒后用后备字体;字体加载完后切换。仅用于 logo、装饰字体等关键场景。

- swap:阻塞期 0 秒,立即用后备字体显示,字体加载完后切换(FOUT)。最常见选择,适合主体文字。

- fallback:阻塞 100ms(短暂 FOIT),然后用后备字体;3 秒切换期内字体到了就切换,超时就放弃。平衡 FOIT 与 FOUT。

- optional:阻塞 100ms,超时就用后备字体,且不再切换(即使字体后来加载到了)。彻底避免布局抖动,代价是慢网用户永远看不到自定义字体。

建议:博客、营销页主文字用 swap;追求零 CLS 的电商可用 optional;logo 用 block。

4. rel=preload 与字体优先级

默认情况下,浏览器只有解析 CSS 时才发现 @font-face 中的字体 URL,再发起请求。这导致字体加载启动晚,关键字体可能到 1.5-2 秒才开始下载。

解决:在 HTML head 中用 link rel="preload" 提示浏览器尽早下载关键字体。

示例:<link rel="preload" href="/fonts/inter-var.woff2" as="font" type="font/woff2" crossorigin>

关键点:crossorigin 必须加(即使字体同源),否则浏览器会重复请求;as="font" 让浏览器赋予正确优先级;type 帮助浏览器筛选支持格式。

注意:只 preload 关键字体(首屏可见的主字体)。preload 太多反而拖慢首屏,因为 preload 会与 HTML、CSS、关键 JS 抢带宽。

5. 可变字体:体积与灵活度的双赢

传统字体一个 family 包含多个 .woff2 文件——Light、Regular、Medium、Bold、Black 等,每个变体都是独立资源。一个完整 Inter 字体家族包含 9 个字重 + 斜体共 18 个文件,总体积 800 KB+。

可变字体(Variable Fonts,2018 年起广泛支持)把所有变体编码到一个文件的「轴」上。轴可以是字重(wght)、字宽(wdth)、斜度(slnt / ital)、视觉尺寸(opsz)等。一个 Inter Variable 文件 200-300 KB 就包含全部变体。

使用:font-weight 不再是 400/700 的离散值,而是 100-900 任意整数(甚至 450、550 这种「半字重」)。设计师能更精细地表达层级。

2026 年大多数主流字体都有 Variable 版本(Inter、Manrope、Noto Sans、思源黑体 Variable)。优先选 Variable。

配合 字体配对工具 选择中英文搭配。

6. 字体子集化:中文字体能用的前提

英文字体完整 ASCII + 扩展拉丁约 100 字符,woff2 体积通常 30-100 KB,全量加载可接受。但中文字体包含 2-3 万汉字 + 标点,思源黑体完整版约 8 MB,直接加载是不可接受的。

子集化(subsetting)从字体中只保留实际用到的字符。三种策略:

- 静态子集:构建时扫描所有 HTML/CSS 文本,生成只包含用到字符的子集。适合内容固定的网站(博客、营销页)。工具:fonttools(Python)、subfont(Node)。

- 动态分包:把常用 3500 字 + 标点打成一个核心包(500 KB),剩余字符按 unicode-range 分成多个小包,浏览器按需下载。Google Fonts 的 Noto 系列默认这种策略。

- CSS unicode-range:在 @font-face 用 unicode-range: U+4E00-9FFF 限定字符范围,浏览器只在页面包含该范围字符时才下载。配合分包策略效果最好。

2026 年的最佳实践:中文站点必须做子集化或分包。简单方案:用 cn-fontsub、font-spider、字蛛等工具自动扫描 + 子集化。

7. Google Fonts vs 自托管:大陆延迟与稳定性

Google Fonts 的优势:海外用户 CDN 速度极快(30-100ms),自动子集化(Noto 系列),版本管理省心,免费。

Google Fonts 的痛点(中国大陆):fonts.googleapis.com 与 fonts.gstatic.com 经常被墙或延迟极高(500ms-2s),有时甚至完全不可达。直接引用会拖慢首屏,部分用户字体加载失败 fallback 到默认字体。

解决方案:

- 自托管:把字体文件放到自己的 CDN(Cloudflare、阿里云、腾讯云),保证国内访问稳定。配合 font-display: swap 即使加载慢也不影响 FCP。

- 国内镜像:loli.net、字体盒等提供 Google Fonts 国内镜像。但稳定性自测,部分镜像近年下线。

- 国内字体服务:字体家、Adobe Fonts(中国版)、有道云字体。商用需注意授权。

建议:面向中国大陆用户的站点一律自托管。海外或多区域站点用 Google Fonts 但配合 preload + swap 兜底。

8. 字体回退栈:让 FOUT 不那么难看

FOUT 的问题是字体切换时布局抖动(CLS 上升)。原因:自定义字体的字宽、行高与后备字体(Arial、PingFang)不同,文字所占像素变化导致后续元素移位。

2026 年的优化技巧:

- size-adjust、ascent-override、descent-override:CSS 字体描述符,让你调整后备字体的度量尺寸接近自定义字体。Chrome 87+ 支持。配合 fontaine、capsize 等工具自动计算最佳值。

- 字体回退栈:font-family: "Inter", -apple-system, "PingFang SC", "Microsoft YaHei", system-ui, sans-serif; 让中英文都有合理 fallback。

- 关键 CSS 内联:把 @font-face 与首屏文字的 CSS 内联到 HTML head,避免 CSSOM 阻塞字体加载启动。

配合 字体配对工具编程字体推荐 选定回退栈。

9. 综合优化方案速查

2026 年一个理想的字体加载流程:

1)选 Variable 字体;2)字符子集化(中文必须);3)woff2 格式;4)自托管到 CDN(国内尤其);5)<link rel="preload"> 关键字体;6)@font-face 用 font-display: swap;7)回退栈用 size-adjust 减少抖动;8)非关键字体延迟到 onload 后加载。

实测:一个中文博客做完上述优化,LCP 从 3.2s 降到 1.1s,CLS 从 0.18 降到 0.03。配合 代码格式化JSON 工具 等加速整体开发。

常见问题

FOIT 和 FOUT 哪个体验更好?

FOUT(Flash of Unstyled Text,先显示后备字体再切换)通常体验更好,因为内容立即可读。FOIT(Flash of Invisible Text,文字隐形直到字体加载完成)会让用户面对空白页面,特别是网络慢时让人误以为页面卡死。Google 与 web.dev 建议默认用 font-display: swap 实现 FOUT。

font-display 应该选 swap、optional 还是 fallback?

主体文字用 swap(保证文字立即可见,字体加载好后切换),追求零布局抖动可用 optional(100ms 内加载不出来就放弃用 fallback)。fallback 介于两者之间,给字体 100ms 阻塞期 + 3 秒切换期。auto 把决策交给浏览器,通常是 FOIT 行为,不推荐。block 仅用于 logo 等少数对字体强依赖的场景。

可变字体能节省多少体积?

一个家族包含多种字重和字宽,传统每个变体都是单独的 woff2 文件,加起来可能 600-800 KB。可变字体把所有变体编码成一个文件中的连续轴,通常 200-300 KB,节省 50-70%。前提是你确实用到多种字重;只用 Regular + Bold 两个变体时可变字体优势缩小。

Google Fonts 在中国大陆能用吗?

不稳定。fonts.googleapis.com 与 fonts.gstatic.com 在大陆经常被墙或延迟极高(500ms-2s),直接引用会拖慢首屏。建议:1)国内站点改用 self-host(自托管)woff2 + font-display: swap;2)或使用国内镜像(loli.net 等,但稳定性需自测);3)或用 Adobe Fonts、字体之家等国内服务。海外站点 Google Fonts 仍是首选。

字体子集化是什么,对中文字体重要吗?

子集化是从原字体中只保留你实际用到的字符,丢弃其余。对英文字体节省有限(完整 ASCII 也就几十 KB),但对中文字体效果惊人——一个完整中文字体 8-15 MB,子集化后保留常用 3500 字 + 标点约 800 KB-1.5 MB,再分包按需加载可降到 200-400 KB / 包。是中文 Web 字体能用的前提。