CSS @font-face 网页字体加载策略

转载:https://zhuanlan.zhihu.com/p/67616144

本文主要介绍网页字体加载策略:

网页字体字体加载策略

@font-face 用法

CSS @font-face 用户定义网站使用的字体,用法如下:

@font-face {
  font-family: 'diyfont';
  src: url('diyfont.eot'); /* IE9+ */
  src: url('diyfont.eot?#iefix') format('embedded-opentype'), /* IE6-IE8 */
       url('diyfont.woff') format('woff'), /* chrome、firefox */
       url('diyfont.ttf') format('truetype'), /* chrome、firefox、opera、Safari, Android, iOS 4.2+*/
       url('diyfont.svg#fontname') format('svg'); /* iOS 4.1- */
}

在浏览器中使用字体需要注意:

  • 需要兼容当前的主流浏览器,需同时使用 TureTpe(.ttf)、Web Open Font Format(.woff)、Embedded Open Type(.eot)、SVG(.svg) 四种字体格式。
  • 嵌入 HTML 文档的字体是指将OpenType字体(压缩的TrueType字体)文件映射到客户端系统,用来提供HTML文档使用该字体,或取代客户端系统已有的同名字体。即让客户端显示客户端所没有安装的字体。
  • .eot(Embedded Open Type) 为 IE 的私有字体格式。Safari3.1 开始支持 .ttf(TrueType) 和 .otf(OpenType)。
  • 未来 .woff(Web Open Font Format) 将会取代 .ttf(TrueType) 和 .otf(OpenType) 两种字体格式。

什么时候会下载字体?

上面讲了字体的基本知识,那你有没有想过,字体是在什么时候下载的呢?当我们仅仅在 CSS 中定义如下样式的时候, 页面加载,字体会自动下载吗?

很遗憾,字体并不会下载。 通常情况下,只有当我们的页面元素用到了 @font-face 中定义的字体的情况下,才会下载对应的字体。

注意: 这里我们说了是通常情况,这是因为,IE8 在只要是定义了 @font-face,即使页面元素没有使用对应的字体,也会下载。

比如在 Firefox 和 IE 9+ 中,遇到如下情况也会下载字体:

<style>
#test {
  font-family: Lato;
}
</style>
<div id="test"></div>

有什么特别之处呢?你可能注意到了,这个元素虽然使用到了 font-family: Lato 样式,但是这个元素并没有任何文本啊!!!。按照我们的理想情况,应该是,只有有文字内容才会去下载字体嘛。而这就是 Chrome, Safari (WebKit/Blink 等) 浏览器的行为。

Chrome, Safari (WebKit/Blink 等)浏览器只有在如下类似情况才会去下载字体:

<style>
#test {
  font-family: Lato;
}
</style>
<div id="test">这里是有文本的哦</div>

总结不同浏览器下载字体的策略:

  • IE8 只要定义了 @font-face,就会去下载字体,不论实际有没有应用该字体。
  • Firefox, IE 9+ 只有定义了 @font-face 并且页面有元素应用了该字体,就会去下载,不论该元素是否有文本内容。
  • Chrome, Safari 只有定义了 @font-face 并且页面有元素应用了该字体,并且该元素有文本内容,才会去下载字体。

如果我们的 DOM 元素是通过 JavaScript 动态插入的呢?比如:

const el = document.createElement('div');
el.style.fontFamily = 'open_sansregular';
document.body.appendChild(el);
el.innerHTML = 'Content.';

结果是一样的,它的下载策略如下:

const el = document.createElement('div');
el.style.fontFamily = 'open_sansregular';
/* 到这里,IE8就会开始下载字体 */

document.body.appendChild(el);
/* 只有到这里,Firefox, IE 9+ 才会开始下载字体 */

el.innerHTML = 'Content.';
/* 只有到这里,Chrome, Safari 才会开始下载字体 */

FOIT(Flash of Invisible Text)

FOIT 是浏览器在加载字体的时候的默认表现形式,也就是在字体加载过程中,页面是看不到文本内容的。在现代浏览器中,FOIT 会导致这种现象出现至多3秒。FOIT 会导致很差的用户体验,这是我们需要尽量去避免的。

FOUT(Flash of Unstyled Text) 与 font-display 属性

FOUT 意思是在字体加载过程中使用默认的系统字体,字体加载完后显示加载的字体,如果超过了 FOIT(3s) 字体还没加载,则继续使用默认的系统字体。

IE 浏览器和 Edge 不会等待 FOIT 超时才显示默认字体,会立即显示默认字体。FOUT 比 FOIT 好,但是需要注意它引起的 reflow.

那么要想使浏览器有 FOUT 行为,我们需要在设置 @font-face 的时候给它加一个属性:font-displayfont-display 默认是 auto, 可选属性与含义如下:

  • auto: The font display policy is user-agent-defined.
  • block: Gives the font face a short block period (3s is recommended in most cases) and an infinite swap period.
  • swap: Gives the font face an extremely small block period (100ms or less is recommended in most cases) and an infinite swap period.
  • fallback: Gives the font face an extremely small block period (100ms or less is recommended in most cases) and a short swap period (3s is recommended in most cases).
  • optional: Gives the font face an extremely small block period (100ms or less is recommended in most cases) and a 0s swap period.

一般设置成 fallbackoptional 即可。

在页面加入下面这个代码以便更快的加载字体:

<link rel="preload" href="font.woff2" as="font" type="font/woff2" crossorigin>

通常和最基本的字体用法配合使用

将字体转化为 BASE64

这种方法就是将 @font-face 中定义字体时的路径直接改为字体的 base64 编码。

优点:这种做法的优点是不会产生 FOIT 和 FOUT。所以也不会有 reflow 和 repaint. 缺点:字体转成 base64 也会很大,会影响页面首次加载速度。不支持逗号分隔的形式加载多种格式的字体,只能加载一种格式字体。这导致你为了尽可能保证所有浏览器都可以兼容,通常会指定为 woff 格式,因为 woff 格式兼容性好,但是却没法使用更小体积的 woff2 格式,因为 woff2 格式兼容性差点。

使用 Font Load API + FOUT + class 切换

这种方式是期初并不使用用到 @font-faceclass,然后用 Font Load API 加载我们想用的字体,然后切换相应的 CSS 即可。Font Load API 是原生的 API:

<script>
document.fonts.load('1em open_sansregular')
.then(function() {
  const docEl = document.documentElement;
  docEl.className += ' open-sans-loaded';
});
</script>

<style>
.open-sans-loaded h1 {
  font-family: open_sansregular;
}
</style>

当然这种方法需要考虑浏览器兼容性的问题。

相关参考

上次更新: 6/7/2019, 1:36:03 PM