# 浏览器怎么加载网页字体

> 适合谁看：想搞清楚网页字体从请求到显示经历了什么、为什么会「白屏」或「跳一下」的前端开发者。

`@font-face` 的基本写法见[《在网页里使用字体：@font-face 的现代写法》](https://docs.cocobook.com/link/133)。本页讲背后的过程，后面几页的优化都从这里出发。

## 一次加载的顺序

1. **下载 HTML，发现 CSS。** 字体 CSS 写在 `<link>` 里会较早开始下载；写在 `@import` 里要等外层 CSS 下载完才开始。
2. **解析 CSS，登记 `@font-face`。** 这一步只是登记「有这么一个字体、文件在哪里」，**不会下载字体文件**。
3. **排版时发现要用它。** 页面上某段文字的 `font-family` 用到了这个字体，并且文字里的字符落在它的 `unicode-range` 里，浏览器才开始下载对应的文件。
4. **等待期间先用别的字体。** 字体没到时，按 `font-display` 的规则，要么先不显示文字，要么先用备用字体显示。
5. **字体到了，重新排版。** 换成新字体后，字宽、行高不同，段落会重新折行，这就是「跳一下」。

所以字体文件开始下载的时间，比 HTML、CSS 都晚，常常排在关键请求的最后面。

## 「白屏」和「跳一下」

| 现象 | 常见叫法 | 原因 |
|---|---|---|
| 文字区域空白一段时间 | FOIT（隐形文字闪烁） | 浏览器在等字体，期间文字不画出来 |
| 先显示系统字体，然后换掉 | FOUT（无样式文字闪烁） | 先用备用字体，字体到了再替换 |

CSS 规范对 `font-display` 的建议是：`block` 最多等约 3 秒；`swap`、`fallback`、`optional` 只等很短的时间（建议不超过 100 毫秒）就先显示备用字体。中文页面字体大，`block` 会让读者盯着空白很久，所以一般用 `swap`，见[《加载策略：font-display、预连接和预加载》](https://docs.cocobook.com/link/1252)。

## 跨域字体要允许跨域

字体文件放在另一个域名时，服务器必须返回 `Access-Control-Allow-Origin` 响应头，否则浏览器会拒绝使用。自己部署字体时要记得配置。

## 中文为什么特别

一款中文字体通常有几万个字形，单个文件常有几 MB 到十几 MB。直接引用整个文件，字体要很久才能显示。解决办法就是把它拆成很多小文件，每个文件用 `unicode-range` 标明包含哪些字，浏览器只下载用到的那几片。原理见[《中文网页字体为什么要拆分子集》](https://docs.cocobook.com/link/598)，动手做见[《动手拆子集：pyftsubset》](https://docs.cocobook.com/link/1249) 和[《按字分片：cn-font-split》](https://docs.cocobook.com/link/1250)。

<p style="margin-top:2.5em;padding-top:.6em;border-top:1px solid #e6e6e6;font-size:.85em;color:#8a8a8a"><a href="https://docs.cocobook.com/link/1307#bkmrk-src-w01-flow" style="color:inherit;text-decoration:none;border-bottom:1px dotted #bbb">参考资料</a></p>