天天看點

網頁端字型加載優化

<b>本文講的是網頁端字型加載優化,</b>

<b></b>

網絡字型加載看起來也許非常複雜,但如果你使用本文的字型加載模式的話,這也并不是一件複雜的事情。你可以将這些模式組合起來,建立一個相容所有浏覽器的字型加載方式。

<a href="https://github.com/xitu/gold-miner/blob/master/TODO/web-font-loading-patterns.md#basic-font-loading">基礎字型加載模式</a>

<a href="https://github.com/xitu/gold-miner/blob/master/TODO/web-font-loading-patterns.md#loading-groups-of-fonts">分組字型加載模式</a>

<a href="https://github.com/xitu/gold-miner/blob/master/TODO/web-font-loading-patterns.md#loading-fonts-with-a-timeout">限制字型加載時間</a>

<a href="https://github.com/xitu/gold-miner/blob/master/TODO/web-font-loading-patterns.md#prioritised-loading">隊列加載模式</a>

<a href="https://github.com/xitu/gold-miner/blob/master/TODO/web-font-loading-patterns.md#custom-font-display">自定義字型顯示行為</a>

<a href="https://github.com/xitu/gold-miner/blob/master/TODO/web-font-loading-patterns.md#optimise-for-caching">為緩存優化</a>

不存在一種普适所有情況的單一模式。選擇一種适合你自己網站的字型加載模式才是最好的。

為了保持模式示例的精簡,這篇文章假設你将網絡字型放在自己的伺服器上。這意味着你的 CSS 檔案中應該有一個或多個<code>@font-face</code> 來定義你希望通過 Font Face Observer 加載的字型。為了簡潔,<code>@font-face</code> 不會出現在所有的模式中,但是你應該假設它們存在。

最基礎的模式就是加載一個或多個獨立的字型。你可以通過為每個字型建立一個單獨的 <code>FontFaceObserver</code> 執行個體,并調用它們的 <code>load</code> 方法來實作。

你也可以在加載多個字型的時候将它們分組:一個組内的字型隻能全部加載成功或是全部加載失敗。如果你加載的字型檔案屬于同一個字型族,且你希望僅在它們全部加載成功時才進行渲染,那麼這種方式将會非常實用。這可以阻止浏覽器在沒能成功加載整個字型族時渲染出糟糕的網頁。

你可以使用 <code>Promise.all</code> 來對字型進行分組。隻有在所有字型都成功加載後 Promise 才會被解析,一旦有某個字型加載失敗,Promise 就會被拒絕。

将字型分組的另一個用途是減少頁面布局的重新計算渲染。如果你逐漸加載和渲染所有字型,浏覽器将會因為網絡字型和降級字型之間不同的尺寸而多次重新計算布局。将字型分組可以把多次計算布局優化為一次。

有些時候字型需要很長時間來加載,但由于字型通常是用于渲染網站的主要内容——文字,長時間的加載就會造成問題。無限制地等待一個字型的加載是不可接受的。你可以通過向字型加載添加一個計時器來解決這個問題。

如下的輔助函數建立了一個計時器,逾時後會傳回一個被拒絕的 Promise.

通過使用 <code>Promise.race</code>,我們可以讓字型加載和計時器“競速”。舉個例子,如果字型在計時器觸發前加載完成,字型就勝利了,Promise 将會被解析。如果計時器在字型加載完成前觸發,Promise 就會被拒絕。

在這個例子中,字型與一個1秒的計時器競速。除了與單個字型競速,計時器還可以與一組字型競速。這是一種簡單而且有效的限制字型加載時間的方法。

通常情況下,隻有部分字型對于渲染首屏内容來說是必要的。在加載其它可選字型之前先加載這些字型,将會極大程度地改善你網站的性能。你可以使用隊列加載模式來實作。

隊列加載模式将會使次要字型依賴于主要字型。如果主要字型加載失敗,次要字型将不會被加載。這會是一個非常重要的特性。

舉個例子,你可以使用隊列加載模式來加載一個小的主要字型以提供有限的支援,之後再加載一個更大的次要字型來提供更多特征和樣式。因為主要字型非常小,它的加載和渲染将會非常快。如果主要字型加載失敗,你可能也不希望加載次要字型,因為其很可能也會加載失敗。

浏覽器顯示網絡字型前需要先通過網絡下載下傳字型,這通常需要一定的時間,并且不同的浏覽器在下載下傳網絡字型時有不同的行為。一些浏覽器在加載字型時隐藏文字,而另一些浏覽器會先顯示降級字型。這兩種方法通常被稱為 Flash Of Invisible Text(FOIT)和 Flash Of Unstyled Text(FOUT)。

網頁端字型加載優化

IE 和 Edge 使用 FOUT,即在網絡字型加載完成之前顯示降級字型。所有其他的浏覽器都使用 FOIT,即在網絡字型加載時隐藏文本。

你可以通過僅在字型棧中放入加載完成的字型來使得使用 FOIT 的浏覽器在加載網絡字型時使用降級字型渲染。如果正在下載下傳的字型不在字型棧中,那些浏覽器就不會試圖隐藏文本。

最簡單的實作方法是在 <code>html</code> 元素上為三個網絡字型加載狀态設定不同的 class:loading(加載中),loaded(加載完成),以及 failed(加載失敗)。

使用這三個 class 和一些簡單的 CSS,你就可以在所有浏覽器中實作 FOUT。我們為所有将要使用網絡字型的元素定義降級字型。當 <code>fonts-loaded</code> class 出現在 <code>html</code> 元素上時,我們通過改變元素的字型棧來應用網絡字型。這将會要求浏覽器加載網絡字型,但是因為這些字型已經下載下傳完成了,渲染操作将能在瞬間完成。

使用這種方法來加載網絡字型可能會讓你想到漸進增強(progressive enhancement),這不是一個巧合。FOUT 就是一種漸進增強。預設的體驗是使用降級字型渲染,然後使用網絡字型來增強體驗。

實作 FOIT 同樣簡單。隻要在網絡字型開始加載時隐藏使用這些字型的内容,當字型加載完成後再重新顯示。注意要記得處理加載失敗的情況,即使網絡字型加載失敗,你的内容應該依然可見。

這樣隐藏内容是否讓你感到不适?對,隐藏内容應該在非常特殊的情況下才被使用,比如你的網絡字型沒有合适的降級字型,或者你知道字型已經被緩存了。

其他的字型加載模式允許你自定義你加載字型的時間和方式。通常情況下,如果字型已經在緩存中,你會希望以不同的方式渲染字型。比如說,當字型已經被緩存時,就不需要先渲染降級字型了。我們可以通過使用 session storage 跟蹤緩存情況的方式來實作。

當一個字型被加載後,我們在 session 中建立一個布爾型标記。這個标記将會保持在整個會話過程中,是以這會是判斷檔案是否在浏覽器緩存中的一個很好的方法。

然後你就可以使用這個資訊以在字型被緩存時改變字型加載政策。比如說,你可以在 <code>head</code> 元素中插入如下的 JavaScript 片段來直接渲染網絡字型。

如果你使用這種方式加載字型,使用者會在第一次通路你的網站時體驗到 FOUT,但是随後的頁面将會直接渲染網絡字型。這樣你既有漸進增強,又不會破壞重複通路者的體驗。

<b>原文釋出時間為:2016年05月11日</b>

<b>本文來自雲栖社群合作夥伴掘金,了解相關資訊可以關注掘金網站。</b>

繼續閱讀