防止自定义字体导致性能下降

Avatar of Chris Coyier
Chris Coyier

DigitalOcean 为您旅程的每个阶段提供云产品。 立即开始使用 200 美元的免费额度!

问题在于 1) 自定义字体很棒,我们想使用它们 2) 自定义字体作为大型附加资源会减慢页面加载速度。最近一直在讨论如何解决这个问题,因此我想总结一些想法并加上我自己的思考。

仅在大屏幕上加载

我看到的第一个想法是 Dave Rupert 关于仅在大屏幕上加载 @font-face 的测试。事实证明,如果您使用 @font-face 但从未应用该字体系列,则不会下载该字体。浏览器很聪明。Dave 的演示。

@font-face {
  font-family: 'Dr Sugiyama';
  font-style: normal;
  font-weight: 400;
  src: local("Dr Sugiyama Regular"), local("DrSugiyama-Regular"), url(http://themes.googleusercontent.com/static/fonts/drsugiyama/v2/rq_8251Ifx6dE1Mq7bUM6brIa-7acMAeDBVuclsi6Gc.woff) format("woff");
}

body {
  font-family: sans-serif;
}
@media (min-width: 1000px) {
  body {
    font-family: 'Dr Sugiyama', sans-serif;
  }
}

Jordan Moore 在 Typekit 博客上有一篇文章 “移动设备上的备用字体”,它采用了相同的思路。

我将此思路应用到我自己的项目中,创建了两个单独的字体套件:一个“完整”字体套件,包含我最初打算使用的所有排版样式,以及一个“轻量级”套件,包含较少的字体(因此重量明显更轻)。我根据用户屏幕的宽度通过 JavaScript 加载这些套件,值基于最小的断点。

使用 Dave 的技术,您无需担心 FOUT(未设置样式文本的闪烁),因为它使用的是原生 @font-face,并且大多数浏览器都已解决了这个问题。我认为 Jordan 的技术更容易出现 FOUT,需要在测试后加载字体,但您可以像以往使用 Typekit 解决 FOUT 一样解决它:在字体加载时使用 visibility: hidden

字体中的 Ajax

如果您的主要关注点是减慢渲染时间(不一定是非完全加载页面的时间),您可以在文档就绪后通过 Ajax 加载包含 @font-face 内容的样式表。Omar Al Zabir 有一个关于此的教程。(感谢 Kevin

$(document).ready(function(){
  $.ajax({
    url: fontFile,
    beforeSend: function ( xhr ) {
      xhr.overrideMimeType("application/octet-stream");
    },
    success: function(data) {
      $("<link />", {
        'rel': 'stylesheet'
        'href': 'URL/TO/fonts.css'
      }).appendTo('head');
    }
  });
});

确保字体文件具有较远的过期标头也很重要。为了在此处解决 FOUT 问题,您需要向 <html> 元素添加一个类(立即使用 JavaScript),并使用它来 visibility: hidden 您想要隐藏的内容,直到字体加载,并在 Ajax 成功回调中将其删除。

延迟加载字体,在缓存后加载后续页面加载

扩展此想法,也许我们只有在非常确定字体文件已缓存的情况下才能显示自定义字体。在后端,我们检查一个 cookie(我们稍后将自行设置),该 cookie 指示字体已缓存。

// Check if cookie exists suggesting fonts are cached

if (fonts_are_cached) {
  echo "<link rel='stylesheet' href='/URL/TO/fonts.css'>";
}

在前端,我们将执行完全相反的操作。如果不存在该 cookie,我们将延迟加载这些字体,然后设置该 cookie。

// Check if cookie exists suggesting fonts are cached

if (!fonts_are_cached) {

  // Don't slow down rendering
  $(window).load(function() {

    // Load in custom fonts
    $.ajax({
      url: 'URL/TO/font.woff'
    });
    $.ajax({
      url: 'URL/TO/font.eot'
    });
    // Don't actually do anything with them, just request them so they are cached.

    // Set cookie indicating fonts are cached

  });
  
}

并非万无一失,因为该 cookie 无法 100% 证明字体已缓存。但是,如果您将其设置为在一天后过期,它仍然有相当大的几率。这里没有 FOUT,因为它要么根本不加载字体,要么使用 @font-face 本地加载。如果您不介意 FOUT(即,无论如何您都希望在第一次页面加载时显示自定义字体),您可以创建 <link> 并插入字体样式表,而不仅仅是请求字体。

另一种方法是将字体的 data URI 版本放入 localStorage 中,并在需要时将其提取出来。您将创建一个 <style> 元素,使用字体的 data URI 版本将 @font-face 代码放入其中,并将其注入。显然,卫报正在尝试这样做。

公平警告,localStorage 可能比缓存慢。


使用 JavaScript 花哨功能的一个可能的优势是知道您需要哪些字体版本。

以及如何。

未来

我们可以获得更多关于客户端情况的信息,所有这些都会变得更好。

他们获得什么样的带宽和延迟?这很难(而且很繁重)进行测试,即使我们可以测试,可靠性也不高。也许 网络信息 API 将在将来有所帮助。

他们的屏幕尺寸是多少?浏览器的功能是什么?我们可以在 JavaScript 中测试这些内容,但如果在服务器端了解这些内容会更好怎么办?也许 Client-Hints 将在将来有所帮助。