解决 Lighthouse 中的 "Ensure text remains visible during webfont load" 问题。

Arjen Karel Core Web Vitals Consultant
Arjen Karel
linkedin

简述 "Ensure text remains visible during webfont load"

网络字体是浏览器默认不可用的字体。这意味着网络字体必须先下载才能使用。在下载网络字体的过程中,网页上的文本会暂时隐藏,直到网络字体加载完成。

因此,页面看起来加载速度会慢很多,因为对访客来说页面还没有"完成"加载。这可能导致 user experience 下降。当你在 Lighthouse 上分析页面时,会出现关于页面加载性能的警告:"Ensure text remains visible during webfont load"。

通过更改 font-display 值或使用字体加载器可以解决此问题。本文将介绍具体方法。

Ensure text remains visible during webfont load

确保加载网络字体时文本保持可见

在网络字体出现之前,网页设计师只能使用少量预装字体。网络字体让你可以在网站上自由使用任何字体。 

这听起来很不错,但网络字体也有缺点;它们会以多种方式降低页面加载速度。

网络字体通常是较大的文件,默认不会安装在计算机上。因此,网络字体必须先下载才能使用。在下载网络字体的过程中,网页上的文本会暂时隐藏,直到网络字体完全加载。这会导致糟糕的 user experience;没有人愿意盯着空白屏幕太久。

网络字体加载并渲染完成后,浏览器会将"不可见文本"替换为使用新网络字体的最终文本。这一时刻被称为 Flash of Invisible Text(FOIT)。正是这个 FOIT 导致了 "Ensure text remains visible during webfont load" 错误消息的出现。

Ensure text remains visible during webfont load

如果你在页面上加载网络字体但没有采取任何措施防止 Flash of Invisible Text,在 Lighthouse 上分析 PageSpeed 时,会出现以下消息:"Ensure text remains visible during webfont load"。它会告诉你在网络字体加载之前让文本可见可以节省多少时间。对于单个字体,这很容易就能节省 100 毫秒。

为什么不可见文本对页面速度不利?

不可见文本实际上不会减慢页面的最终测量加载时间。那么为什么 Lighthouse 认为这是一个大问题? 

Google 认为网页提供最佳 user experience 非常重要。通过尽快在页面上显示内容,可以改善 user experience。比较下面我们首页的两个胶片视图:

Flash of Invisible TextFOIT met een webfont

使用 display:swap 消除 Flash of Invisible TextGeen FOIT met een webfont

如你所见,两个页面完成加载的时间完全相同。然而,后一个版本的网站对访客来说看起来要好得多。访客可以立即开始阅读。

这就是为什么明智的做法是无论如何都显示文本——不是使用最终字体,而是使用 "fallback" 字体。这样访客会觉得你的页面加载速度确实非常快

简要回顾:FOIT 和 FOUT

在继续之前,有必要区分以下概念:FOIT 和 FOUT。FOIT 代表 Flash of Invisible Text,当网络字体在加载过程中不可见时会发生。你可以通过添加 fallback 字体来缓解这个问题。当 fallback 字体被网络字体替换时,这被称为 FOUT,即 Flash of Unstyled Text。

使网络字体在加载时可见

有两种方法可以使网络字体在加载时可见:第一种是通过 CSS font-display 值;第二种是通过类使用 fallback 字体。两种方法各有优缺点,下面将详细讨论。

方法一:Font-display:swap

Font-display 是所有现代浏览器都支持的 CSS 描述符。font-display 描述符根据字体是否已下载以及何时下载来决定字体的显示方式。Font-display 用于 @font-face 规则中。 

Font-display 有不同的值:block、swap、fallback 和 optional。使用 swap 值可以避免 FOIT,让文本尽快出现在屏幕上。 

一旦我们在 @font-face 规则中设置了 font-display: swap 值,在页面加载过程中系统默认字体将被使用,直到网络字体加载完成。这有助于访客立即阅读页面上的文本

Google fonts

使用 Google fonts 时,你可以通过在样式表或嵌入代码中简单添加 "&display=swap" 来使用 font-display: swap 方法。  

<!-- 通过外部样式表 -->
<link href="https://fonts.googleapis.com/css?family=Open+Sans&display=swap" rel="stylesheet">
<!-- 通过 import 方法 -->
<style>
 @import url ('https://fonts.googleapis.com/css?family=Open+Sans&display=swap); 
</style>

顺便说一下,我们并不推荐 Google fonts。自行托管网络字体几乎总是更快。它让你对字体的"预加载"过程有更多控制。你可以利用已有的 http/2 连接,而且不需要下载额外的样式表。

本地字体

你在使用本地字体吗?(很好!这比 Google fonts 快得多。)那么你可以在 @font-face 规则中添加 font-display: swap。

@font-facefont-family: "Open Sans";  
 font-weight: 400; 
 font-style: normal; 
 src: url("OpenSans400.woff2") format("woff2"); 
 
}

方法二:通过类使用字体

使字体在加载时可见的第二种方法是使用类。这些类通常(但并非总是)添加到 <body> 或 <html> 元素上。

这种方法的优点是你对 fallback 字体和 Flash of Unstyled Text 的时机有更多控制。

这种方法的工作原理如下:在样式表中指定页面初始应使用某种字体(fallback 字体)进行渲染。然后通过 JavaScript FontFace API 或预加载来加载网络字体。字体加载完成后,向页面添加一个类。该类确保网络字体被激活

你可能会问,为什么要这样做?这样做是为了获得对 fallback 字体的更多控制。你可以使用更大的行间距或不同的字号来显示 fallback 字体,使其更好地匹配网络字体。这可以防止布局偏移。

使用多种网络字体时,你可以使用 FontFace API 方法一次切换所有字体。这节省了大量浏览器重绘。就个人而言,我不推荐这种方法;这确保了 FOUT 在最后一个字体加载完成后才发生。所以这总是比必要的时间更晚。

通过 FontFace API 使用带类的字体:

使用带类字体的第一种方式是通过 FontFace API。通过 JavaScript 加载网络字体。字体加载完成后,添加一个类。
<style>
  //fallback 字体,使用 .9rem 字号
  html{
    font-family: sans-serif;
    font-size:.9rem;
  }  

  //网络字体,使用 1rem 字号
  html.fl{
    font-family: 'webfont';
    font-size:1rem;
  }
</style>

<script>
var font = new FontFace("webfont", "url(/font.woff2)", {
  style: 'normal', unicodeRange: 'U+000-5FF', weight: '400'
});

// 不要等待渲染树,立即发起请求!
font.load().then(function() {
  document.fonts.add(font);
  document.documentElement.classList.add("fl")
});
</script>

通过预加载链接

第二种方法是通过预加载链接。如下所述预加载字体。完成后,切换 <html> 元素的类。

<link 
  rel="preload" 
  href="/webfont.woff2" 
  as="font" 
  type="font/woff2" crossorigin
  onload="document.documentElement.classList.add('fl')">

<style>
  //fallback 字体,使用 .9rem 字号
  html{
    font-family: sans-serif;
    font-size:.9rem;
  }  

  //网络字体,使用 1rem 字号
  html.fl{
    font-family: 'webfont';
    font-size:1rem;
  }

  //fontface,在 .fl 类添加到 html 标签后才会激活
  @font-face{
    font-family:'Open Sans';
    font-style:normal;
    font-weight:400;
    font-display:swap;
    src: url(/webfont.woff2) format("woff2");
    unicode-range:U+000-00FF;
  }</style>

额外提示和技巧

  1.    始终预加载 https://www.corewebvitals.io/nl/pagespeed/ensure-text-remains-visible-during-web font-load? 可见的字体。字体默认不会在使用之前下载。确定需要网络字体吗?那就预加载它,以便更早可用。
  2. 想要完全避免 FOIT 和 FOUT?使用 font-display: optional 结合预加载。
  3. 自行托管网络字体总是比通过 Google fonts 或其他外部 CDN 更快。

Performance is a Feature.

Treating speed as an afterthought fails. Build a performance culture with a dedicated 2-sprint optimization overhaul.

Initialize Project >>

  • 2-Sprint Overhaul
  • Culture Building
  • Sustainable Speed
修复 Ensure text remains visible during webfont loadCore Web Vitals 修复 Ensure text remains visible during webfont load