最佳化 WebFont 載入和算繪

Ilya Grigorik
Ilya Grigorik

包含您可能不需要的所有樣式變化版本的「full」WebFont,以及可能會未使用的所有字符,都可能輕鬆導致下載超過 MB。這篇文章將說明如何改善 WebFonts 的載入,讓訪客只下載要使用的內容。

為解決大型檔案包含所有變化版本的問題,@font-face CSS 規則是專為可將字型系列分割為資源集合。例如萬國碼 (Unicode) 子集和不同的樣式變化版本。

根據這些宣告,瀏覽器會判斷所需的子集和變化版本,並下載轉譯文字所需的最少集合,使用起來相當方便。然而,如果不留意,這也可能會在關鍵轉譯路徑中造成效能瓶頸,並延遲文字轉譯。

預設行為

延遲載入字型具有重大隱藏影響,可能會導致文字延遲顯示。瀏覽器必須建構轉譯樹狀結構,此樹狀結構取決於 DOM 和 CSSOM 樹狀結構,才能知道轉譯文字需要哪些字型資源。因此,在其他重要資源之後,字型要求會一直延遲,且瀏覽器在擷取資源之前可能會無法轉譯文字。

字型重要轉譯路徑

  1. 瀏覽器要求 HTML 文件。
  2. 瀏覽器會開始剖析 HTML 回應並建構 DOM。
  3. 瀏覽器會探索 CSS、JS 和其他資源,並調派要求。
  4. 瀏覽器收到所有 CSS 內容後,便會建構 CSSOM,並與 DOM 樹狀結構結合,以建構算繪樹狀結構。
    • 字型要求會在轉譯樹狀結構後發送,指出需要哪種字型變化版本才能轉譯網頁指定文字。
  5. 瀏覽器會執行版面配置,並將內容繪製到螢幕上。
    • 如果字型無法使用,瀏覽器可能無法顯示任何文字像素。
    • 字型可用後,瀏覽器會繪製文字像素。

網頁內容首次繪製之間的「賽程」(可在建構轉譯樹狀結構後不久執行),而字型資源的要求則是「空白文字問題」,亦即瀏覽器可能會轉譯網頁版面配置,卻省略任何文字。

藉由預先載入 WebFonts 並使用 font-display 控制瀏覽器在無法使用字型上的行為後,就能避免空白頁面和版面配置因為字型載入而變動。

預先載入 WebFont 資源

如果您的網頁很有可能需要使用預先已知的網址代管的特定 WebFont,則可採用資源優先順序。使用 <link rel="preload"> 會在關鍵轉譯路徑中提早觸發 WebFont 要求,不必等待系統建立 CSSOM。

自訂文字顯示延遲

預先載入功能更有機會在網頁轉譯時取得 WebFont,但不保證一定能使用 WebFont。使用 font-family 目前不支援的文字轉譯文字時,您還是需要考量瀏覽器的行為。

在「避免在字型載入期間隱藏文字」文章中,您可以看到預設瀏覽器的行為不一致。 不過,您可以使用 font-display 讓新式瀏覽器瞭解您希望瀏覽器的行為。

瀏覽器支援

  • 60
  • 79
  • 58
  • 11.1

資料來源

與某些瀏覽器實作的現有字型逾時行為類似,font-display 會將字型下載的生命週期劃分為三個主要時段:

  1. 第一個句號是字型區塊期間。在這段期間,如果未載入字型,任何嘗試使用該字型的元素都必須改為顯示隱藏式備用字型。如果字型在區塊期間成功載入,系統隨後會正常使用字型。
  2. 字型交換週期緊接在字型區塊週期結束後就會立即發生。在這段期間,如果未載入字型,任何嘗試使用該字型的元素都必須改為顯示備用字型。如果字型在交換期間成功載入,系統就會正常使用字型。
  3. 字型失敗期限會立即出現在字型替換期過後。如果在這段期間內尚未載入字型,系統會將其標示為失敗的載入,導致一般字型遞補。否則,系統會正常使用字型。

瞭解這些週期,您就可以使用 font-display 根據下載或下載時間決定字型的顯示方式。

如要使用 font-display 屬性,請將其新增至 @font-face 規則:

@font-face {
  font-family: 'Awesome Font';
  font-style: normal;
  font-weight: 400;
  font-display: auto; /* or block, swap, fallback, optional */
  src: local('Awesome Font'),
       url('/fonts/awesome-l.woff2') format('woff2'), /* will be preloaded */
       url('/fonts/awesome-l.woff') format('woff'),
       url('/fonts/awesome-l.ttf') format('truetype'),
       url('/fonts/awesome-l.eot') format('embedded-opentype');
  unicode-range: U+000-5FF; /* Latin glyphs */
}

font-display 目前支援下列值範圍:

  • auto
  • block
  • swap
  • fallback
  • optional

如要進一步瞭解如何預先載入字型,以及 font-display 屬性,請參閱下列文章:

字型載入 API

如果搭配使用 <link rel="preload"> 和 CSS font-display,您可充分控製字型的載入和轉譯方式,而且不會增加太多負擔。不過,如果您需要額外的自訂項目,且願意讓執行 JavaScript 造成負擔,則可選擇其他方法。

字型載入 API 提供指令碼介面,可讓您定義及操控 CSS 字型、追蹤下載進度,以及覆寫預設的延遲載入行為。舉例來說,如果您確定需要特定字型變化版本,可以定義該字型,並指示瀏覽器立即擷取字型資源:

瀏覽器支援

  • 35
  • 79
  • 41
  • 10

資料來源

var font = new FontFace("Awesome Font", "url(/fonts/awesome.woff2)", {
  style: 'normal', unicodeRange: 'U+000-5FF', weight: '400'
});

// don't wait for the render tree, initiate an immediate fetch!
font.load().then(function() {
  // apply the font (which may re-render text and cause a page reflow)
  // after the font has finished downloading
  document.fonts.add(font);
  document.body.style.fontFamily = "Awesome Font, serif";

  // OR... by default the content is hidden,
  // and it's rendered after the font is available
  var content = document.getElementById("content");
  content.style.visibility = "visible";

  // OR... apply your own render strategy here...
});

此外,由於您可以檢查字型狀態 (透過 check()) 方法並追蹤下載進度,因此也可以定義在網頁上顯示文字的自訂策略:

  • 您可以保留所有文字轉譯,直到字型顯示為止。
  • 您可以為每個字型設定自訂逾時。
  • 您可以使用備用字型來解除封鎖轉譯,並在取得字型後,插入使用所需字型的新樣式。

最棒的是,您也可以針對網頁上的不同內容,混搭上述策略。 舉例來說,您可以延遲部分區段的文字轉譯時間,直到字型可用為止、使用備用字型,然後在字型下載完畢後重新轉譯。

必須適當地快取快取

字型資源通常是不會經常更新的靜態資源。因此,這些參數非常適合用於長時間效期上限,請務必為所有字型資源指定條件式 ETag 標頭最佳 Cache-Control 政策

如果您的網頁應用程式使用服務工作站,則使用快取優先策略提供字型資源適用於大多數用途。

請勿使用 localStorageIndexedDB 儲存字型,每種字型各有一組效能問題。瀏覽器的 HTTP 快取提供最佳、最強大的機制來將字型資源提供給瀏覽器。

WebFont 載入檢查清單

  • 使用 <link rel="preload">font-display 或 Font Loading API 自訂字型載入和轉譯:預設的延遲載入行為可能會導致文字轉譯延遲。這些網路平台功能可讓您針對特定字型覆寫這個行為,並為網頁上的不同內容指定自訂轉譯和逾時策略。
  • 指定重新驗證和最佳快取政策:字型是不常更新的靜態資源。確認您的伺服器提供長期 max-age 時間戳記和重新驗證權杖,以允許在不同頁面之間有效重複使用字型。如果您使用服務工作站,就適合採用快取優先策略。

使用 Lighthouse 自動測試 WebFont 載入行為

Lighthouse 可協助您自動執行相關程序,確保您遵循網站字型的最佳做法。

以下稽核有助於確保網頁持續遵循網站字型最佳化的最佳做法: