字體預加載如何加速網頁?Web Font Preload 與 Core Web Vitals 優化實作

Published on: | Last updated:

最近有個小困擾...關於網頁字體

嗯...最近在弄一些網頁的東西,然後一直覺得哪裡卡卡的。後來發現,是字體載入的問題。 🤔

你們應該有碰過吧?就是網頁打開了,內容都出來了,但上面的字,先是長得像「新細明體」或「標楷體」之類的系統預設字體,過個零點幾秒,才「啪!」一下,變回設計師精心挑選的那個漂亮字體。

那個瞬間的「跳動」,老實說... 有點煩。技術上好像叫 FOUT (Flash of Unstyled Text)。 還有更糟的是 FOIT (Flash of Invisible Text),就是字體下載好之前,你根本看不到任何文字,一片空白。 這兩種狀況,對使用者體驗都不太好,而且還會影響 Google 的一個評分,叫 Core Web Vitals

所以,解法是?先跟瀏覽器「打小報告」

我研究了一下,發現一個滿簡單直接的方法,叫做「字體預加載」(Font Preload)。

簡單講,它就是在網頁的 `` 區塊,放一個特殊的 `` 標籤。這標籤就像是提早跟瀏覽器打小報告:「喂,等等會用到一個很重要的字體檔,你現在沒事就先去下載,不要等到 CSS 都解析完了才去拿。」

程式碼大概長這樣:

<head>
  ...
  <link rel="preload" href="/fonts/your-custom-font.<a href="https://sia.codes/posts/making-google-fonts-faster/" target="_blank" class="blogHightLight_css nobox">woff2</a>" as="font" type="font/woff2" crossorigin>
  ...
</head>

這裡有幾個重點:

  • `rel="preload"`:這就是主角,告訴瀏覽器「預先載入」這個資源。
  • `as="font"`:這個很重要,明確告訴它這是個字體檔,瀏覽器會用比較高的優先級去處理它。
  • `type="font/woff2"`:這是字體的格式,`woff2` 是目前壓縮率最高、最推薦的網頁字體格式。
  • `crossorigin`:如果你的字體是放在別的伺服器上(例如 Google Fonts),那就一定要加這個屬性,不然瀏覽器會因為安全考量而忽略你的預加載指令。
簡化版的載入流程示意圖
簡化版的載入流程示意圖

這樣一來,瀏覽器在解析 HTML 的時候,就已經開始下載字體了,而不是等到讀完 CSS 才知道需要這個字體。理論上,這能縮短文字出現或變換的等待時間,進而改善 LCP (最大內容繪製) 這個 Core Web Vitals 指標。

不只 preload,還要搭配 `font-display` 屬性

只做 `preload` 其實只對了一半。另一個關鍵是 CSS 裡的 `@font-face` 規則,裡面有個屬性叫 `font-display`。 這個屬性是告訴瀏覽器,在自訂字體還沒下載完成時,該怎麼辦。

我把它整理成一個表,這樣比較好懂,用口語化的方式說:

`font-display` 的值 發生什麼事? (使用者體感) 適合用在哪?
`block` 「...字咧?」畫面一片空白,等個幾秒字才跟內容一起出現。很容易造成 FOIT。 老實說...現在很少推薦用了。除非那個字體是 Logo 或非看不可的元素,不然等待的體驗不太好。
`swap` 「哦有字了!...咦?」會先看到系統預設字體,然後「啪」一聲換成載入好的美美字體。這就是 FOUT。 這是目前最常見的做法,先求有再求好。可以最快讓使用者看到內容,但缺點就是那個「跳動」可能會造成版面位移 (CLS)。
`fallback` 跟 `swap` 很像,但它會先空白個 100 毫秒左右,如果字體還沒好,就先用系統字。但如果等了大概 3 秒字體還沒來,它就放棄了,那一頁就一直用系統字了。 算是一種折衷。想用自訂字體,但又不想等太久。如果網路慢,就犧牲美觀換取穩定性。
`optional` 也是先空白一下下 (100毫秒),如果字體還沒好,就直接用系統字,而且「不會」再切換回來了。 效能優先!對於網路狀況不好的使用者最友善。如果這個字體只是點綴,不是非有不可,用這個就對了。保證不會有 CLS。

所以現在比較主流的作法是 `preload` 加上 `font-display: swap`,盡量讓字體早點載入,縮短那個「啪」一下的 FOUT 時間。

在開發者工具中檢查字體載入
在開發者工具中檢查字體載入

但... `swap` 真的那麼完美嗎?

我自己試了之後,覺得 `font-display: swap` 有個小陷阱。😬

雖然它讓文字很快出現,但因為「預設字體」和「你的網頁字體」通常長得不一樣寬、不一樣高,在切換的那個瞬間,整段文字的排版可能會亂掉。 比如原本只有一行的標題,換了字體後變成兩行,把下面的內容都往下推。這個「推擠」的動作,就是另一個 Core Web Vitals 指標:CLS (版面配置轉移)。

Google 的 web.dev 網站有提到,這是在效能和美感之間的取捨。 甚至在國外論壇像 Reddit 或 GitHub 上,都有開發者在抱怨 `swap` 造成的 CLS 問題很惱人。

特別是像日文或中文這種字元複雜的字體,一個檔案就很大,載入時間更久,`swap` 帶來的跳動感可能會更明顯。 這點跟英文字體那種幾十 KB 的小檔案狀況很不一樣。

所以,我的結論是...

想了一下,我覺得沒有「最好的」方法,只有「最適合的」。

  1. 最重要的字體才 Preload:不要貪心,把所有字體都預加載。`preload` 會佔用瀏覽器的頻寬,如果你預加載了一個其實沒那麼快用到的字體,反而會拖慢其他更重要的資源(比如圖片或 JS)。 像是網頁一打開第一眼就會看到的大標題字體,就值得 `preload`。
  2. 品牌或 Logo 字體用 `swap` 或 `fallback`:這些是體現品牌風格的關鍵,值得稍微犧牲一點點穩定性來確保它們能被顯示。
  3. 內文、長篇文章用 `optional`:對於一大段的內文,閱讀的流暢性比字體美觀更重要。用 `optional` 可以確保在網路不好的情況下,使用者能順暢閱讀,而且完全不會有版面跳動的問題。
  4. 終極大絕招:調整你的備用字體,讓它的大小、行高盡量跟你要用的網頁字體差不多。這樣就算發生 `swap`,跳動的幅度也會小很多,CLS 分數就會比較好看。

總之,這東西還真不是加一行 `preload` 就沒事了。😅 需要考慮的細節還不少。

優化前後的體感對比
優化前後的體感對比

不知道大家有沒有被這個字體閃爍問題困擾過?或者有什麼更好的優化技巧?可以在下面留言分享一下你的看法~ 👇

Related to this topic:

Comments

  1. profile
    Guest 2025-07-02 Reply
    聽起來好像很厲害,不過我還是有點懷疑。每個網站情況不同吧?感覺這種通用方案可能沒那麼靈驗。難道不是要具體問題具體分析嗎?有點像濫用雞湯感覺。
  2. profile
    Guest 2025-04-24 Reply
    我覺得字體預載雖然能幫助加快網站速度,但如果過度依賴,可能會造成其他優化策略的衝突。其實還是需要全面考量整體網站效能,才能真正提升SEO排名啊!