CSS @font-face性能優化

概述

本文主要介紹字體加載優化的經常使用策略,大部份內容爲引用和翻譯。javascript

1、 font-face基本用法

font-face的基本用法想必你們都是知道的,基本上就是相似這樣:css

@font-face {
	font-family: Lato;
	src: url('font-lato/lato-regular-webfont.woff2') format('woff2'),
		 url('font-lato/lato-regular-webfont.woff') format('woff'),
		 url(font-lato/lato-regular-webfont.ttf) format("opentype");
}
p { font-family: Lato, serif; }
複製代碼

這樣就可使咱們的網頁用上自定義字體了。 除了font-family 和 src屬性以外,還擁有font-style以及font-weight屬性。 src能夠指定多種字體,會按順序依次適用,好比上面的示例中會先加載woff2字體,若是失敗再加載woff字體,不然加載opentype字體。 src所支持的字體能夠有如下類型:html

src參數帶不帶引號均可以,參數的格式不一樣含義也不盡相同,好比下面:java

src: url(fonts/simple.woff);       /* 加載simple.woff,地址相對於樣式表的地址 */
src: url(/fonts/simple.woff);      /* 加載simple.woff,地址是網站的絕對地址 */
src: url(fonts/coll.otc#foo);      /* 從coll.otc字符集中加載foo字體 */
src: url(fonts/coll.woff2#foo);    /* 從coll.woff2字符集中加載foo字體 */
src: url(fonts.svg#simple);        /* 加載id 爲'simple'的SVG字體 */
複製代碼

src中加載的字體地址受跨域的約束,若是想跨域加載字體,須要設置CORS。git

這就是font-face的最基礎的用法。 接下來咱們會進一步分析font-face的用法,並儘量的找出優化策略。github

2、 何時會下載字體?

上面講了字體的基本知識,那你有沒有想過,字體是在何時下載的呢?當咱們僅僅在CSS中定義以下樣式的時候, 頁面加載,字體會自動下載嗎?web

@font-face {
	font-family: Lato;
	src: url('font-lato/lato-regular-webfont.woff2') format('woff2'),
		 url('font-lato/lato-regular-webfont.woff') format('woff'),
		 url(font-lato/lato-regular-webfont.ttf) format("opentype");
}
複製代碼

很遺憾,字體並不會下載。 一般狀況下,只有當咱們的頁面元素用到了font-face中定義的字體的狀況下,纔會下載對應的字體。c#

注意: 這裏咱們說了是一般狀況,這是由於,IE8在只要是定義了font-face,即便頁面元素沒有使用對應的字體,也會下載。跨域

在其它瀏覽器中也不盡相同,瀏覽器

好比在FirefoxIE 9+ 中,遇到以下狀況也會下載字體:

html

<div id="test"></div>
複製代碼

css

#test {
	font-family: Lato;
}
複製代碼

有什麼特別之處呢? 你可能注意到了,這個元素雖然使用到了font-family: Lato樣式,可是這個元素並無任何文本啊!!!。 按照咱們的理想狀況,應該是,只有有文字內容纔會去下載字體嘛。 而這就是Chrome, Safari (WebKit/Blink 等)瀏覽器的行爲。

Chrome, Safari (WebKit/Blink 等)瀏覽器只有在以下相似狀況纔會去下載字體:

html

<div id="test">這裏是有文本的哦</div>
複製代碼

css

#test {
	font-family: Lato;
}
複製代碼

因此總結一下,不一樣瀏覽器下載字體的策略:

  • IE8 只要定義了font-face,就會去下載字體,不論實際有沒有應用該字體。
  • Firefox, IE 9+ 只有定義了font-face 而且頁面有元素應用了該字體,就會去下載,不論該元素是否有文本內容。
  • Chrome, Safari 只有定義了font-face 而且頁面有元素應用了該字體,而且該元素有文本內容,纔會去下載字體。

那你可能會問了,若是咱們的DOM元素是經過動態插入的呢?好比:

var el = document.createElement('div');
el.style.fontFamily = 'open_sansregular';
document.body.appendChild(el);
el.innerHTML = 'Content.';
複製代碼

答案是同樣的,它的下載策略以下:

var el = document.createElement('div');
el.style.fontFamily = 'open_sansregular';
/* 到這裏,IE8就會開始下載字體 */

document.body.appendChild(el);
/* 只有到這裏,Firefox, IE 9+ 纔會開始下載字體 */

el.innerHTML = 'Content.';
/* 只有到這裏,Chrome, Safari 纔會開始下載字體 */
複製代碼

3、 FOIT(Flash of Invisible Text)

FOIT是瀏覽器在加載字體的時候的默認表現形式,也就是在字體加載過程當中,頁面是看不到文本內容的。在現代瀏覽器中,FOIT會致使這種現象出現至多3秒。FOIT會致使不好的用戶體驗,這是咱們須要儘可能去避免的。

4、 FOUT(Flash of Unstyled Text) 與 font-display屬性

FOUT意思是在字體加載過程當中使用默認的系統字體,字體加載完後顯示加載的字體,若是超過了FOIT(3s)字體還沒加載,則繼續使用默認的系統字體。

IE瀏覽器和Edge不會等待FOIT超時才顯示默認字體,會當即顯示默認字體。FOUT比FOIT好,可是須要注意它引發的reflow.

那麼要想使瀏覽器有FOUT行爲,咱們須要在設置@font-face的時候給它加一個屬性:font-display。 font-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.

通常設置成fallback和optional便可。

5、 preload

在頁面加入下面這個代碼以便更快的加載字體:

<link rel="preload" href="font.woff2" as="font" type="font/woff2" crossorigin>
複製代碼

一般和最基本的字體用法配合使用

6、 字體轉 BASE64URI

這種方法就是將@font-face中定義字體時的路徑直接改成字體的base64編碼。

優勢: 這種作法的優勢是不會產生FOIT和FOUT。因此也不會有reflow和repaint. 缺點: 字體轉成base64也會很大,會影響頁面首次加載速度。不支持逗號分隔的形式加載多種格式的字體,只能加載一種格式字體。這致使你爲了儘量保證全部瀏覽器均可以兼容,一般會指定爲woff格式,由於woff格式兼容性好,可是卻無法使用更小體積的woff2格式,由於woff2格式兼容性差點。

7、異步加載BASE64格式URI字體

這種方法就是經過異步的方式插入帶有BASE64格式URI字體的CSS連接。

8、使用Font Load API + FOUT + class切換

這種方式是期初並不使用用到@font-face的class,而後用Font Load API加載咱們想用的字體,而後切換相應的CSS便可。Font Load API是原生的API:

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

.open-sans-loaded h1 {
	font-family: open_sansregular;
}
複製代碼

固然這種方法須要考慮瀏覽器兼容性的問題。

9、 FOFT(Flash of Faux Text)

FOFT會把字體的加載分紅多個部分,首先加載羅馬網絡字體,而後會在加載真實的粗體和斜體的時候當即使用font-synthesis屬性渲染粗體和斜體的變體。

這種方法是基於[使用Font Load API + FOUT + class切換]這種方式的,很是適合加載同一種字體可是不一樣粗細,字形的場景,好比羅馬、粗體、斜體、粗斜體等。咱們將這些字體分紅2階段: 第一階段是羅馬字體,而後當即渲染人造粗體和斜體,最後(第二階段)用真實字體替代。這裏面還可使用sessionStorage優化訪問重複視圖的場景。

10、CRITICAL FOFT

CRITICAL FOFT和標準的FOFI的惟一區別就在於第一階段羅馬字體的加載,CRITICAL FOFT不會加載羅馬字體的全集,只會加載它的一個子集(好比A-Za-z0-9),全集會在第二階段加載。

11、CRITICAL FOFT WITH DATA URI

這個和CRITICAL FOFT的惟一區別就是羅馬子集字體的加載方式,前面是用Font Load API完成了,這裏會將馬子集字體硬編碼成BASE64 URI的形式加載。

12、 CRITICAL FOFT WITH PRELOAD

這個同上面的惟一區別仍是第一階段羅馬子集字體的加載方式,它採用的是preload的形式加載。

結論

總的字體加載的策略能夠用這個圖總結以下:

參考文獻

本文主要翻譯自以下博客文章

  1. https://www.zachleat.com/web/comprehensive-webfonts/#font-display
  2. https://dev.opera.com/articles/better-font-face/

《IVWEB 技術週刊》 震撼上線了,關注公衆號:IVWEB社區,每週定時推送優質文章。

相關文章
相關標籤/搜索