一个“完整”WebFont 包括您可能并不需要的所有样式变体,以及可能不会用到的所有字形,很容易就会产生几兆字节的下载。在这篇博文中,您将了解如何优化 WebFonts 的加载,以便访问者仅下载他们要使用的内容。
为了解决包含所有变体的大型文件的问题,专门设计了 @font-face
CSS 规则,用于将字体系列拆分为一个资源集合。例如,Unicode 子集和不同的样式变体。
鉴于这些声明,浏览器会确定所需的子集和变体,并下载渲染文本所需的最小集,非常方便。但是,如果您不小心,它也可能会在关键渲染路径中形成性能瓶颈并延迟文本渲染。
默认行为
延迟加载字体会产生一个重要的隐藏影响,即可能会延迟文本渲染。浏览器必须先构建渲染树(依赖于 DOM 树和 CSSOM 树),然后才能知道需要哪些字体资源来渲染文本。因此,字体请求的处理要远远延迟其他关键资源,并且在获取资源之前,可能会阻止浏览器渲染文本。
- 浏览器请求 HTML 文档。
- 浏览器开始解析 HTML 响应和构建 DOM。
- 浏览器会发现 CSS、JS 和其他资源并分派请求。
- 浏览器在收到所有 CSS 内容后构建 CSSOM,然后将其与 DOM 树合并以构建渲染树。
- 待渲染树指示需要哪些字体变体才能在网页上渲染指定文本后,系统才会分派字体请求。
- 浏览器执行布局并将内容绘制到屏幕上。
- 如果字体尚不可用,浏览器可能不会渲染任何文本像素。
- 字体可用后,浏览器将绘制文本像素。
网页内容的首次绘制(可在渲染树构建后不久完成)与字体资源请求之间的“竞态”导致了“空白文本问题”,在这种情况下,浏览器可能会渲染页面布局,但会省略所有文本。
通过预加载 WebFonts 并使用 font-display
控制浏览器在使用不可用字体时的行为,您可以防止因加载字体而导致空白页面和布局偏移。
预加载 WebFont 资源
如果您的网页很有可能需要在您事先知道的网址上托管特定 WebFont,那么您可以利用资源优先级。
使用 <link rel="preload">
会在关键渲染路径早期触发对 WebFont 的请求,而无需等待 CSSOM 创建。
自定义文本呈现延迟
虽然预加载可以提高 WebFont 在网页内容呈现时可用的可能性,但并不保证一定如此。
您仍然需要考虑浏览器在渲染使用 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
属性,请参阅以下博文:
字体加载 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 应用使用 Service Worker,则通过缓存优先策略提供字体资源适用于大多数用例。
您不应使用 localStorage
或 IndexedDB 存储字体;每种格式都有自己的一套性能问题。浏览器的 HTTP 缓存提供了最好且最可靠的机制来向浏览器提供字体资源。
WebFont 加载核对清单
- 使用
<link rel="preload">
、font-display
或 Font Loading API 自定义字体加载和渲染:默认的延迟加载行为可能会导致文本渲染延迟。借助这些 Web 平台功能,您可以替换特定字体的这种行为,并为网页上的不同内容指定自定义渲染和超时策略。 - 指定重新验证和最佳缓存政策:字体是不经常更新的静态资源。确保您的服务器提供长期的 max-age 时间戳和重新验证令牌,以实现不同页面之间高效的字体重复使用。如果使用 Service Worker,则适合采用缓存优先策略。
使用 Lighthouse 自动测试 WebFont 加载行为
Lighthouse 可以帮助自动执行流程,确保您遵循网页字体优化最佳实践。
以下审核可帮助您确保网页始终遵循网页字体优化最佳实践: