Skip to content

網頁字型與 SEO:中文字型的體積、FOIT 與 FOUT

網頁字型與 SEO:中文字型體積、FOIT 與 FOUT

字型是中文網站最容易被忽略的效能問題。一個英文網站載入三種字重可能只多花 100KB,同樣的事在中文網站上是 好幾 MB

更麻煩的是,字型同時影響兩個 核心指標:文字晚顯示會拖累 LCP ,換字時的重新排版會計入 CLS

中文字型為什麼這麼大

差別在字數。

字形數量單一字重壓縮後
拉丁字母字型約 200 以內20-40KB
繁體中文常用字約 13,0001-3MB
中日韓統一表意文字20,000 以上4MB 以上

每一個字都是獨立的向量輪廓,字數多寡直接決定檔案大小。所以中文字型不是「壓縮一下就好」,而是必須 只載入真的會用到的字

FOIT 與 FOUT

字型還沒載入完的那段時間,瀏覽器要決定文字怎麼顯示,兩種選擇各有名字。

現象全名行為後果
FOITFlash of Invisible Text字型載入完成前 不顯示文字一段空白,使用者讀不到任何東西
FOUTFlash of Unstyled Text先用備援字型顯示,載入完再換換字時閃一下、可能位移

中文網站應該選 FOUT

FOIT 在英文網站上可能只是 100 毫秒的空白,感覺不出來。但中文字型要載入數 MB,那段空白可能長達數秒,使用者面對的是一片空白的頁面,而 LCP 也會一併被算進去。

先用系統字型把內容顯示出來,至少讀得到。

控制這件事的是 font-display 屬性,各個值的行為差異見 字型顯示 font-display

三種對策

對策解決什麼難度
子集化檔案太大
font-display空白與閃爍
預載字型太晚開始下載

三者不是替代關係,而是要 一起用:子集化把檔案變小、font-display 決定載入期間怎麼顯示、預載讓它早點開始下載。

子集化

把字型切成多個分片,瀏覽器只下載頁面實際用到的那幾片:

css
/* 每一片宣告自己負責的字碼範圍 */
@font-face {
  font-family: "MyFont";
  src: url("/fonts/myfont-1.woff2") format("woff2");
  unicode-range: U+4E00-4FFF;
  font-display: swap;
}
@font-face {
  font-family: "MyFont";
  src: url("/fonts/myfont-2.woff2") format("woff2");
  unicode-range: U+5000-5FFF;
  font-display: swap;
}

也可以走另一條路:掃過網站所有文字,只產生 實際出現過的字。靜態網站特別適合這招,常能把數 MB 壓到 100KB 以內。做法見 中文字型子集化

font-display

css
@font-face {
  font-family: "MyFont";
  src: url("/fonts/myfont.woff2") format("woff2");
  font-display: swap; /* 立刻用備援字型,載完再換 */
}

預載

字型的下載通常晚一步,瀏覽器要先解析 CSS、算出哪些元素用到這個字型,才會開始下載。用 preload 可以讓它提早開始:

html
<link
  rel="preload"
  href="/fonts/myfont.woff2"
  as="font"
  type="font/woff2"
  crossorigin
>

preload 不要濫用

每個 preload 都在跟其他資源搶頻寬。只預載 首屏一定會用到 的那一個字型檔,預載了五個字重反而會拖慢 LCP 。細節見 資源提示

減少換字時的位移

備援字型與目標字型的字寬、行高不同時,換字的瞬間文字會重新排列,把下方內容往下推,這會計入 CLS

css
/* 調整備援字型的度量,讓它接近目標字型 */
@font-face {
  font-family: "FallbackAdjusted";
  src: local("Microsoft JhengHei");
  size-adjust: 97%;
  ascent-override: 88%;
  descent-override: 12%;
}

body {
  font-family: "MyFont", "FallbackAdjusted", sans-serif;
}

還有一個選項:不用網頁字型

系統字型堆疊是完全合理的選擇

沒有額外請求、沒有閃爍、沒有位移、沒有第三方依賴。如果品牌沒有強制指定字型,這是最省事的做法。

css
body {
  font-family:
    system-ui, -apple-system, "Segoe UI", "Noto Sans TC",
    "PingFang TC", "Microsoft JhengHei", sans-serif;
}

省下的時間可以拿去優化 圖片 ,那邊的回報通常更大。

這一組還有這些主題

主題說明
字型顯示 font-displayswapoptionalfallback 的行為差異與預載時機
中文字型子集化unicode-range 分片、依實際用字產生子集、woff2 壓縮
阻塞渲染的 CSS 與 JavaScript關鍵樣式內嵌、deferasync 的選擇

常見問題

中文字型為什麼這麼大?

字數差距造成的。英文字型只需要幾十個字母加標點,總共不到兩百個字形;中文光是常用字就上萬個,涵蓋整個中日韓統一表意文字區更超過兩萬。每個字都是獨立的向量輪廓,所以單一字重壓縮後仍常在數 MB 以上。

FOIT 和 FOUT 哪一個比較好?

多數情況選 FOUT。它先用系統字型把文字顯示出來,字型載入完再換掉,使用者至少讀得到內容;FOIT 則是在字型載入完成前完全不顯示文字,中文字型動輒數秒,那段空白對體驗與 載入速度指標 都不利。

不用網頁字型是不是最快?

就效能而言確實是。改用系統字型堆疊沒有任何額外請求,不會有閃爍也不會有版面位移。如果品牌沒有強制指定字型,這是最省事的選擇,可以把力氣放在別的優化上。

字型會影響版面穩定度嗎?

會。備援字型與目標字型的字寬、行高不同時,換字的瞬間文字會重新排列,把下方內容推移,計入 累積版位偏移 。可以用 size-adjust 這類調整備援字型度量的屬性讓兩者對齊,減少換字時的跳動。

Google Fonts 和自架哪個好?

各有取捨。Google Fonts 省事且已做好分片,但多一次跨網域連線;自架能完全掌控子集內容與快取策略,也少一個第三方依賴,但要自己處理分片與更新。以中文字型來說,自架加上自訂子集通常能壓到更小,比較見 中文字型子集化

延伸閱讀

參考資料:web.dev:Best practices for fonts