包含所有样式变体(您可能不需要)以及所有可能未使用的字形的“完整”WebFont 很容易导致下载量达到数兆字节。在本文中,您将了解如何优化 Web 字体的加载,以便访问者仅下载他们将使用的字体。
为了解决包含所有变体的大型文件问题,@font-face
CSS 规则专门设计为让您可以将字体系列拆分为一系列资源。例如,Unicode 子集和不同的样式变体。
有了这些声明,浏览器便可确定所需的子集和变体,并下载呈现文本所需的最小集,非常方便。不过,如果您不小心,这也可能会在关键渲染路径中造成性能瓶颈,并延迟文本渲染。
默认行为
延迟加载字体有一个重要的隐含含义,可能会延迟文本呈现。 浏览器必须先构建渲染树(该树依赖于 DOM 和 CSSOM 树),然后才能知道需要哪些字体资源来渲染文本。因此,字体请求会在其他关键资源之后延迟很长时间,并且在提取资源之前,浏览器可能会被阻止呈现文本。
- 浏览器请求 HTML 文档。
- 浏览器开始解析 HTML 响应并构建 DOM。
- 浏览器会发现 CSS、JS 和其他资源,并调度请求。
- 浏览器会在收到所有 CSS 内容后构建 CSSOM,并将其与 DOM 树结合起来构建渲染树。
- 在渲染树指明需要哪些字体变体才能在网页上渲染指定文本后,系统会分派字体请求。
- 浏览器会执行布局并将内容绘制到屏幕上。
- 如果字体尚不可用,浏览器可能无法渲染任何文本像素。
- 字体可用后,浏览器会绘制文本像素。
页面内容的首次绘制(可以在构建渲染树后不久完成)与对字体资源的请求之间的“争用”会导致“空白文本问题”,即浏览器可能会渲染页面布局,但省略所有文本。
通过预加载 Web Fonts 并使用 font-display
控制浏览器在无法使用字体时的行为方式,您可以防止因字体加载而导致空白页面和布局偏移。
预加载 WebFont 资源
如果您的网页很可能会需要托管在您事先知道的网址上的特定 WebFont,您可以利用资源优先级功能。使用 <link rel="preload">
会在关键渲染路径的早期触发对 WebFont 的请求,而无需等待 CSSOM 创建。
自定义文本渲染延迟时间
虽然预加载可提高 Web 字体在网页内容呈现时可用性的可能性,但无法保证一定能做到。您仍然需要考虑浏览器在渲染使用尚不可用的 font-family
的文本时会如何表现。
在避免在字体加载期间显示不可见文本一文中,您可以看到默认浏览器行为不一致。不过,您可以使用 font-display
指示现代浏览器应如何运行。
与某些浏览器实现的现有字体超时行为类似,font-display
会将字体下载生命周期划分为三个主要阶段:
- 第一个周期是字体块周期。在此期间,如果未加载字体,则任何尝试使用该字体的元素都必须改用不可见的回退字体进行渲染。如果字体在屏蔽期间成功加载,则会正常使用该字体。
- 字体切换期会在字体块期之后立即发生。在此期间,如果未加载字体,则任何尝试使用该字体的元素都必须改用回退字体进行渲染。如果字体在换货期内成功加载,则系统会正常使用该字体。
- 字体故障期会在字体切换期结束后立即发生。如果此时段开始时字体样式尚未加载,则会被标记为加载失败,从而导致正常的字体回退。否则,系统会正常使用字体。
了解这些时间段意味着,您可以使用 font-display
来决定字体应如何呈现,具体取决于字体是否已下载以及下载的时间。
如需使用 font-display
属性,请将其添加到 @font-face
规则中:
@font-face {
font-family: 'Awesome Font';
font-style: normal;
font-weight: 400;
font-display: auto; /* or block, swap, fallback, optional */
src: local('Awesome Font'),
url('/fonts/awesome-l.woff2') format('woff2'), /* will be preloaded */
url('/fonts/awesome-l.woff') format('woff'),
url('/fonts/awesome-l.ttf') format('truetype'),
url('/fonts/awesome-l.eot') format('embedded-opentype');
unicode-range: U+000-5FF; /* Latin glyphs */
}
font-display
目前支持以下值范围:
auto
block
swap
fallback
optional
如需详细了解如何预加载字体以及 font-display
属性,请参阅以下文章:
Font Loading API
将 <link rel="preload">
和 CSS font-display
搭配使用,您可以对字体加载和呈现进行极大控制,而不会增加太多开销。不过,如果您需要进行额外的自定义,并且愿意承担运行 JavaScript 带来的开销,则可以选择另一种方式。
Font Loading API 提供了一个脚本接口,用于定义和操作 CSS 字体,跟踪其下载进度,以及替换其默认的延迟加载行为。例如,如果您确定需要特定的字体变体,可以定义该变体并告知浏览器立即提取字体资源:
var font = new FontFace("Awesome Font", "url(/fonts/awesome.woff2)", {
style: 'normal', unicodeRange: 'U+000-5FF', weight: '400'
});
// don't wait for the render tree, initiate an immediate fetch!
font.load().then(function() {
// apply the font (which may re-render text and cause a page reflow)
// after the font has finished downloading
document.fonts.add(font);
document.body.style.fontFamily = "Awesome Font, serif";
// OR... by default the content is hidden,
// and it's rendered after the font is available
var content = document.getElementById("content");
content.style.visibility = "visible";
// OR... apply your own render strategy here...
});
此外,由于您可以通过 check()
方法查看字体状态并跟踪其下载进度,因此您还可以定义在网页上呈现文本的自定义策略:
- 您可以暂停所有文本渲染,直到字体可用为止。
- 您可以为每个字体实现自定义超时。
- 您可以使用回退字体取消屏蔽渲染,并在字体可用后注入使用所需字体的新样式。
最重要的是,您还可以针对网页上的不同内容组合使用上述策略。例如,您可以延迟某些部分的文本渲染,直到字体可用为止,使用回退字体,然后在字体下载完成后重新渲染。
必须进行适当的缓存
字体资源通常是静态资源,不会频繁更新。因此,它们非常适合设置较长的 max-age 到期时间。请确保为所有字体资源指定有条件的 ETag 标头和最佳 Cache-Control 政策。
如果您的 Web 应用使用服务工件,则对于大多数用例,使用缓存优先策略来提供字体资源是合适的。
您不应使用 localStorage
或 IndexedDB 存储字体;这两者各有各自的性能问题。浏览器的 HTTP 缓存提供了一种最优且最稳健的机制,可将字体资源传送到浏览器。
WebFont 加载核对清单
- 使用
<link rel="preload">
、font-display
或 Font Loading API 自定义字体加载和呈现:默认的延迟加载行为可能会导致文本呈现延迟。借助这些 Web 平台功能,您可以针对特定字体替换此行为,并为网页上的不同内容指定自定义渲染和超时策略。 - 指定重新验证和最佳缓存政策:字体是更新频率较低的静态资源。请确保您的服务器提供长效的最大有效期时间戳和重新验证令牌,以便在不同网页之间高效地重复使用字体。如果使用的是服务 worker,则适合采用缓存优先策略。
使用 Lighthouse 自动测试 WebFont 加载行为
Lighthouse 可以帮助您自动执行确保您遵循 Web 字体优化最佳实践的流程。
以下审核可帮助您确保您的网页长期遵循 Web 字体优化最佳实践: