字型的最佳做法

根據 Core Web Vitals 最佳化網頁字型。

本文將討論字型的效能最佳做法。網頁字型會以多種方式影響效能:

本文件分為三個部分:字型載入字型傳送字型算繪。各節會說明字型生命週期的特定層面運作方式,並提供相應的最佳做法。

載入字型

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

如果不確定網頁的字型是否及時要求,請在 Chrome 開發人員工具的「網路」面板中,查看「時間軸」分頁的詳細資訊。

開發人員工具中的「時間軸」分頁。

瞭解 @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 資源提示時,請注意字型供應商可能會從不同來源提供樣式表和字型。舉例來說,以下是 Google Fonts 使用 preconnect 資源提示的方式。

<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 宣告,如果謹慎使用,應該只會用來載入單一字型格式。

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

字型傳送

字型傳送速度越快,文字算繪速度就越快。此外,如果字型提早交付,有助於消除因字型替換而導致的版面配置位移。

使用自行代管的字型

從理論上來說,使用自行代管的字型應能提升效能,因為這樣就不必設定第三方連線。實際上,這兩種選項的效能差異並不明顯。舉例來說,根據 網路年鑑的調查結果,使用第三方字型的網站比使用第一方字型的網站,算繪速度更快。

如果您考慮使用自行代管的字型,請確認網站使用內容傳遞網路 (CDN)HTTP/2。如果沒有使用這些技術,自行代管字型的效能不太可能提升。

如果您使用自行代管的字型,建議您也套用一些字型檔案最佳化設定,第三方字型供應商通常會自動提供這些設定。例如字型子集化和 WOFF2 壓縮。套用這些最佳化措施所需的工作量,取決於網站支援的語言。請特別注意,為中日韓語言最佳化字型可能特別困難。

使用 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 範圍的字元,系統就會下載字型檔案。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: system-ui

可變字型的概念是,單一可變字型可取代多個字型檔案。可變字型會定義「預設」字型樣式,並提供用於操控字型的「軸」。舉例來說,如果使用具有 Weight 軸的變數字型,即可實作字體,而這類字體先前需要使用淺色、一般、粗體和特粗體等不同字型。

並非所有人都適合改用變數字型。 可變字型包含許多樣式,因此通常比只包含一種樣式的個別非可變字型檔案大。如果網站使用 (且需要使用) 多種字型樣式和粗細,改用可變字型後,效能提升幅度最大。

字型顯示

如果瀏覽器遇到尚未載入的網路字型,就會面臨兩難:是否要等到網路字型載入完畢,再算繪文字?還是應該在網頁字型載入前,先以備用字型顯示文字?

不同瀏覽器處理這個情況的方式不同。根據預設,如果相關聯的網頁字型尚未載入,以 Chromium 為基礎的瀏覽器和 Firefox 瀏覽器會封鎖文字轉譯最多 3 秒。Safari 會無限期封鎖文字算繪。

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

選擇適當的 font-display 策略

font-display 會告知瀏覽器,在相關聯的網頁字型尚未載入時,應如何繼續進行文字算繪。這是依字型定義。

@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 的 SVG 精靈頁面Material Icons 指南

減少備用字型與網路字型之間的差異

如要減少 CLS 影響,可以使用 size-adjust 屬性

結論

網頁字型仍是效能瓶頸,但我們有越來越多的選項,可盡量減少這個瓶頸。