跳转到主要内容

浏览器怎么加载网页字体

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

@font-face 的基本写法见《的基本写法见《在网页里使用字体:@font-face 的现代写法》。本页讲背后的过程,后面几页的优化都从这里出发。

一次加载的顺序

  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、预连接和预加载》。

跨域字体要允许跨域

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

中文为什么特别

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

参考资料