字型的最佳做法

針對網站體驗核心指標最佳化調整網頁字型。

Katie Hempenius
Katie Hempenius

本文將說明字型效能最佳做法。網路字型會影響效能的情況有很多種:

本文分為三個部分:字型載入字型提交字型算繪。每個章節都會說明字型生命週期中該特定部分的運作方式,並提供相應的最佳做法。

字型載入

字型通常是重要資源,因為如果沒有字型,使用者可能無法查看網頁內容。因此,字型載入的最佳做法通常著重於確保字型盡早載入。請特別留意從第三方網站載入的字型,因為下載這些字型檔案需要個別的連線設定。

如果不確定網頁的字型是否已按時要求,請在 Chrome 開發人員工具的「Network」面板中查看「Timing」分頁,瞭解詳情。

開發人員工具中的「Timing」分頁螢幕截圖

瞭解 @font-face

在深入瞭解字型載入最佳做法前,請先瞭解 @font-face 的運作方式,以及這會如何影響字型載入。

@font-face 宣告是使用任何網頁字型時的重要部分。至少會宣告用來參照字型的名稱,並指出對應字型檔案的位置。

@font-face {
  font-family: "Open Sans";
  src: url("/fonts/OpenSans-Regular-webfont.woff2") format("woff2");
}

常見的誤解是,遇到 @font-face 宣告時就會要求字型,但這並非事實。@font-face 宣告本身不會觸發字型下載作業。相反地,只有在網頁上使用的樣式參照字型時,才會下載字型。例如:

@font-face {
  font-family: "Open Sans";
  src: url("/fonts/OpenSans-Regular-webfont.woff2") format("woff2");
}

h1 {
  font-family: "Open Sans"
}

換句話說,在上述範例中,只有在網頁包含 <h1> 元素時,才會下載 Open Sans

因此,在考慮字型最佳化時,請務必將樣式表與字型檔案視為同等重要。變更樣式表的內容或交付方式,會對字型送達時產生重大影響。同樣地,移除未使用的 CSS 和分割樣式表,也可以減少網頁載入的字型數量。

內嵌字型宣告

大部分網站都會在主要文件的 <head> 中內嵌字型宣告和其他重要樣式,這麼做比在外部樣式表中加入這些項目更有益。這樣一來,瀏覽器就不必等待外部樣式表下載,因此可以更快發現字型宣告。

<head>
  <style>
    @font-face {
        font-family: "Open Sans";
        src: url("/fonts/OpenSans-Regular-webfont.woff2") format("woff2");
    }

    body {
        font-family: "Open Sans";
    }

    ...etc.

  </style>
</head>

內嵌重要 CSS 是較進階的技術,並非所有網站都能實現。雖然這麼做有明顯提升成效的好處,但還需要額外的程序和建構工具,才能確保必要的 CSS (最好是重要的 CSS) 內嵌正確,而且任何其他 CSS 能以不轉譯的封鎖方式傳送。

預先連線至重要的第三方來源

如果您的網站會載入第三方網站的字型,強烈建議您使用 preconnect 資源提示,及早連線至第三方來源。資源提示應置於文件的 <head> 中。下方的資源提示會設定連線,以便載入字型樣式表。

<head>
  <link rel="preconnect" href="https://fonts.com">
</head>

如要預先連結用於下載字型檔案的連線,請新增使用 crossorigin 屬性的個別 preconnect 資源提示。有別於樣式表,字型檔案必須透過 CORS 連線傳送。

<head>
  <link rel="preconnect" href="https://fonts.com">
  <link rel="preconnect" href="https://fonts.com" crossorigin>
</head>

使用 preconnect 資源提示時,請注意字型供應程式可能會提供來自不同來源的樣式表和字型。舉例來說,以下是 preconnect 資源提示如何用於 Google 字體。

<head>
  <link rel="preconnect" href="https://fonts.googleapis.com">
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
</head>

使用 preload 載入字型時需小心

雖然 preload 可在網頁載入程序的早期階段讓字型可供探索,但代價是會從載入其他資源中移除瀏覽器資源。

內嵌字型宣告並調整樣式表可能會是更有效的做法。這些調整可更有效地解決未及時發現的字型問題,而非只是提供解決方法。

此外,使用 preload 做為字型載入策略時也應謹慎為之,因為這會略過瀏覽器內建的部分內容協商策略。舉例來說,preload 會忽略 unicode-range 宣告,如果謹慎使用,則應只用於載入單一字型格式。

不過,使用外部樣式表時,預先載入最重要的字型非常有效率,因為瀏覽器必須等到之後才會發現該字型是否需要用到。

字型提交

更快的字型提交速度可加快文字算繪速度。此外,如果字型提前提供,就能避免因字型切換而導致的版面配置位移。

使用自行代管的字型

理論上,使用自架字型應可提供更好的效能,因為這樣可省去第三方連線設定。但實際上,這兩種選項的成效差異較不明確。舉例來說,Web Almanac 發現使用第三方字型的網站轉譯速度,會比使用第一方字型的字型更快。

如果您考慮使用自行代管的字型,請確認網站使用內容傳遞網路 (CDN)HTTP/2。如果不使用這些技術,自架字型就很難提供更佳效能。詳情請參閱「內容傳送網路」。

如果您使用自架字型,建議您也自動套用第三方字型供應商通常提供的部分字型檔案最佳化功能,例如字型子集和 WOFF2 壓縮。要採用這些最佳化做法,所需的努力程度會因網站支援的語言而異。請特別注意,針對 CJK 語言最佳化字型並不容易。

使用 WOFF2

在現代字型中,WOFF2 是最新的字型,支援的瀏覽器最廣泛,且提供最佳壓縮功能。由於 WOFF2 採用 Brotli 技術,因此將壓縮結果比 WOFF 多了 30%,可減少下載的資料量,進而提升執行效能。

考量到瀏覽器支援情形,專家現在建議只使用 WOFF2:

事實上,我們也認為是時候宣稱也是很重要的一點:只使用 WOFF2,則不用理會其他東西。

這樣一來,CSS 和工作流程會大幅簡化,而且不會意外下載重複或錯誤的字型。現已全面支援 WOFF2。因此,除非您需要支援非常古老的瀏覽器,否則請直接使用 WOFF2。如果無法做到,建議您完全不要向這些舊版瀏覽器提供任何網頁字型。如果您已採用可靠的備用策略,就不會發生這個問題。舊版瀏覽器的訪客只會看到備用字型。

Bram Stein,摘自 2022 年網路年鑑

子集字型

字型檔案通常會包含大量字形,用於支援的各種字元。不過,您不一定要使用網頁上的所有字元,而且可以透過子設定字型縮減字型檔案。

@font-face 宣告中的 unicode-range 描述符會告知瀏覽器字型可用於哪些字元。

@font-face {
    font-family: "Open Sans";
    src: url("/fonts/OpenSans-Regular-webfont.woff2") format("woff2");
    unicode-range: U+0025-00FF;
}

如果頁面含有一或多個符合萬國碼範圍的字元,系統就會下載字型檔案。unicode-range 通常會根據網頁內容使用的語言,提供不同的字型檔案。

unicode-range 通常會與子集技術搭配使用。子集字型包含原始字型檔案中較少的字形。舉例來說,網站可能會為拉丁文和西里爾文產生個別的子集字型,而不是為所有使用者提供所有字元。每個字型中的符號數量差異極大:拉丁字型通常每個字型有 100 到 1000 個符號;中日韓字型則可能有超過 10,000 個字元。移除未使用的字形,可大幅縮減字型的檔案大小。

部分字型供應商可能會自動提供不同版本的字型檔案,並包含不同的子集。舉例來說,Google Fonts 預設會執行以下操作:

/* devanagari */
@font-face {
  font-family: 'Poppins';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url(https://fonts.gstatic.com/s/poppins/v20/pxiEyp8kv8JHgFVrJJbecnFHGPezSQ.woff2) format('woff2');
  unicode-range: U+0900-097F, U+1CD0-1CF6, U+1CF8-1CF9, U+200C-200D, U+20A8, U+20B9, U+25CC, U+A830-A839, U+A8E0-A8FB;
}
/* latin-ext */
@font-face {
  font-family: 'Poppins';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url(https://fonts.gstatic.com/s/poppins/v20/pxiEyp8kv8JHgFVrJJnecnFHGPezSQ.woff2) format('woff2');
  unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
  font-family: 'Poppins';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url(https://fonts.gstatic.com/s/poppins/v20/pxiEyp8kv8JHgFVrJJfecnFHGPc.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

改用自行託管時,這項最佳化功能很容易遭到偵測,且會在本機產生較大的字型檔案。

如果字型供應商允許,您也可以手動建立字型子集,方法是透過 API (Google Fonts 透過提供 text 參數支援這項功能),或手動編輯字型檔案,然後自行代管。可產生字型子集的工具包括 subfontglyphanger。不過,請檢查您使用的字型是否允許子集和自架站。

減少使用網路字型

最快顯示的字型是一開始沒有要求的字型。系統字型和可變字型提供兩種可能,可減少網站所用網頁字型數量的方法。

系統字型是使用者裝置使用者介面使用的預設字型。系統字型通常會因作業系統和版本而異。由於字型已安裝,因此不需要下載字型。系統字型特別適合用於內文。

如要在 CSS 中使用系統字型,請將 system-ui 列為字型系列:

font-family: system-ui

可變字型的概念是單一可變字型可以用來取代多個字型檔案。變數字型會定義「預設」字型樣式,並提供「軸」來操控字型。舉例來說,如果要實作字母,以往需要使用輕型、一般、粗體和特粗體等不同字型,但現在只要使用具有 Weight 軸線的可變字型即可。

並非所有人都會從改用可變字型中受益。可變字型包含許多樣式,因此檔案大小通常會比只包含一種樣式的個別非可變字型還大。使用變數字型的網站,如果採用 (或需要採用) 多種字型樣式和粗細,將可獲得最大改善。

字型轉譯

當遇到尚未載入的網路字型時,瀏覽器會面臨兩難的處境:是否應在網路字型載入前,先暫緩轉譯文字?或者,在網路字型到達之前,應以備用字型轉譯文字?

不同瀏覽器處理這個情境的方式不盡相同。根據預設,如果未載入相關網頁字型,以 Chromium 和 Firefox 的瀏覽器最多會封鎖文字顯示功能 3 秒;Safari 會無限期停止顯示文字。

您可以使用 font-display 屬性設定這項行為。這個選項可能會造成重大影響:font-display 可能會影響 LCP、FCP 和版面配置穩定性。

選擇適當的 font-display 策略

font-display 會告知瀏覽器,在未載入相關網頁字型時,應如何進行文字轉譯。每個 font-face 都會定義這個屬性。

@font-face {
  font-family: Roboto, Sans-Serif
  src: url(/fonts/roboto.woff) format('woff'),
  font-display: swap;
}

font-display 有五個可能的值:

封鎖期間 替換期間
自動 因瀏覽器而異 因瀏覽器而異
封鎖 2 到 3 秒 無限
交換 0 毫秒 無限
備用 100 毫秒 3 秒
選用 100 毫秒
  • 封鎖期間:封鎖期間會在瀏覽器要求網路字型時開始。在封鎖期間,如果無法使用網路字型,系統會以不可見備用字型轉譯字型,因此使用者無法看到文字。如果所選字型在封鎖期間結束後無法使用,系統就會以備用字型顯示該字型。
  • 切換週期:交換週期晚於區塊期間。如果網路字型在換用期間可供使用,系統就會「換用」該字型。

font-display 策略反映了效能與美觀之間的取捨。因此,我們很難提供建議做法,因為這取決於個人偏好、網頁和品牌對網路字型的重視程度,以及換用較晚到達的字型時,會造成多大的視覺衝擊。

對大多數網站而言,以下三種策略最適用:

  • 如果效能是首要考量:請使用 font-display: optional。這是最「高效」的做法:文字算繪延遲時間不超過 100 毫秒,且保證不會有與字型交換相關的版面配置偏移。然而,這個缺點是延遲抵達時不會使用網路字型。

  • 如果您最重視快速顯示文字,但仍想確保使用的是網路字型:請使用 font-display: swap,但請務必提早提供字型,以免造成版面配置偏移。這個選項的缺點是,當字型延遲到達時,會造成明顯的轉換。

  • 如果確保文字以網頁字型顯示是首要之務:請使用 font-display: block,但請務必提早提供字型,以便盡量減少文字延遲時間。缺點是,初始文字顯示會延遲。請注意,即使發生此情況,這仍可能會在文字實際繪製時造成版面配置位移,而因此會使用備用字型空間來預留空間。載入網路字型後,可能需要不同的空間,因此需要轉換。不過,這可能會比 font-display: swap 更簡潔的移位,因為文字本身不會改變。

請注意,這兩種方法可以合併使用:例如,將 font-display: swap 用於品牌和其他視覺上獨特的網頁元素;將 font-display: optional 用於內文使用的字型。

縮小備用字型和網頁字型之間的間隔

如要降低 CLS 影響,您可以使用新的 size-adjust 屬性。詳情請參閱CSS 相關文章size-adjust。這是我們工具組中最新加入的功能,因此目前較為進階,且需要手動操作。但日後不妨多方嘗試,觀察日後可用的工具改良方法!

結論

網路字型仍是效能瓶頸,但我們有越來越多選項可用於最佳化,盡可能減少這個瓶頸。