字型的最佳做法

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

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

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

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

如果不確定網頁的字型是否已按時要求,請在 Chrome 開發人員工具的「Network」面板中查看「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 是最新的字型,支援的瀏覽器最廣泛,且提供最佳壓縮功能。由於使用 Brotli,WOFF2 的壓縮率比 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 個字形。
  • CJK 字型可能包含超過 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:

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

圖示字型

font-display 策略適用於傳統網頁字型,但不適用於圖示字型。圖示字型的備用字型通常與圖示字型外觀大不相同,且其字元可能會傳達完全不同的意思。因此,圖示字型更有可能造成版面配置大幅變動。

此外,使用備用字型可能不切實際。盡可能使用 SVG 取代圖示字型,這對無障礙體驗也更為有利。一般來說,熱門圖示字型的新版本通常支援 SVG。如要進一步瞭解如何改用 SVG,請參閱「Font Awesome」和「Material Design 圖示」。

減少備用字型和網頁字型之間的差異

如要降低 CLS 影響,您可以使用 size-adjust 屬性

結論

雖然網路字型仍是效能瓶頸,但我們提供越來越多選項,讓我們能夠盡可能最佳化,以減少這個瓶頸。