繁體中文網站最容易被忽略的效能成本,往往不是第三方腳本,而是字型。
一套完整的 CJK 字型可能超過 10MB;如果把它整包送給每位訪客,手機使用者會先等字型下載,才看到穩定的文字。這篇整理 2026 年比較實用的做法:先用系統字體扛住大部分內文,再把真正需要品牌風格的字型做子集化。少載一點,咖啡就能早一點喝到。
先判斷:你真的需要 Web Font 嗎?
如果網站是內容型部落格、管理後台或文件站,最划算的選擇通常是系統字體優先。系統字體不需要額外下載,對 LCP、FCP 和行動流量都比較友善:
body {
font-family:
system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
"PingFang TC", "Hiragino Sans GB", "Microsoft JhengHei",
"Noto Sans CJK TC", sans-serif,
"Apple Color Emoji", "Segoe UI Emoji";
}macOS 與 iOS 通常會使用 PingFang TC,Windows 則多半 fallback 到 Microsoft JhengHei。如果你不需要完全一致的品牌字形,這一步可能比任何字型工具更有效。
三種字型策略怎麼選?
| 情境 | 建議做法 | 取捨 |
|---|---|---|
| 內容型部落格 | 全站系統字體 + font-display: optional | 最快,但不同平台字形不完全一致 |
| 品牌官網或 Landing Page | 標題使用子集化 Web Font,內文用系統字 | 兼顧美觀與速度,需維護字元清單 |
| 需要固定品牌字形的產品 UI | 自架 WOFF2 + 動態或靜態子集化 | 控制力最高,但要加入 build 流程 |
不要為了「看起來專業」就讓整篇內文載入完整 Noto Sans TC。先把 Web Font 限制在 H1、H2 或少量 UI 元件,通常更容易得到可接受的效能與一致性。
為什麼繁中字型特別容易拖慢首屏?
中文字型需要涵蓋數千到數萬個漢字、標點與不同字重,完整檔案常達 10MB 以上。以一個實際靜態站案例來看,Google Fonts 曾依 Unicode 範圍送出 23 個請求、共約 1.78MB;改成依站內實際用字自架後,該案例收斂為 4 個請求。這是特定頁面與測試條件的案例,不是所有網站都會得到相同數字。
外部 CDN 通常還會多出 fonts.googleapis.com 與 fonts.gstatic.com 的連線設定成本;但自架不代表一定更快,還要看 CDN、HTTP 版本、CSS 發現時機與實際字型分片。瀏覽器快取也可能受分區策略影響,跨站共用快取的優勢不能直接當成保證,因此「同網域自架」值得納入實測比較。
子集化到底在做什麼?
子集化就是從完整字型裡只留下網站真的會用到的字,再輸出較小的 WOFF2。
有兩種常見模式:
- 靜態子集化:掃描一頁內容,只保留這頁的字。檔案最小,適合單頁 Landing Page;但內容一改就要重跑。
- Unicode-range 分片:把字型切成許多小碎片,CSS 用
unicode-range宣告每個碎片負責的範圍。瀏覽器會依頁面文字與範圍判斷需要哪些分片;一旦某分片匹配,通常會下載整個分片,而不是只下載其中一個字,適合內容持續增加的網站。
子集裡沒有的字不會自動變成方框。瀏覽器會沿著 font-family 堆疊找下一個可用字型;真正的代價是那個字可能和周圍文字有些風格差異。
自架子集化的實作流程
1. 保留 Latin 與站內實際 CJK 字元
掃描文章、模板與導覽文字,收集漢字、全形標點和英文數字。Latin 子集則建議沿用 Google Fonts 原本的字元範圍,避免英文、貨幣符號或常用標點突然 fallback。
2. 用工具產出 WOFF2
cn-font-split 適合把大字型切成 Unicode 分片;如果想依整個網站實際用字產出單一子集,也可以使用 subset-font,其底層是 HarfBuzz hb-subset 的 WASM 封裝。
概念上的流程如下:
掃描 .md / .njk
↓
收集 Latin + CJK 字元
↓
subset-font / cn-font-split
↓
輸出 .woff2
↓
CSS @font-face 掛載3. 用 @font-face 控制載入行為
@font-face {
font-family: "Noto Sans TC";
src: url("/fonts/noto-sans-tc.woff2") format("woff2");
font-weight: 400 700;
font-style: normal;
font-display: swap;
}swap 會先顯示系統字,再替換成 Web Font,適合品牌標題;內容型網站若更在意避免文字跳動,可考慮 optional,讓載入不及時的訪客直接維持系統字。實際 block/swap 時間是瀏覽器依規格建議值實作,不是固定保證。
4. 把它接進 build 流程
新增文章或修改導覽列後,重新執行例如 pnpm run subset-fonts。原始字型可以快取,所以通常不需要每次重新下載;重點是讓「新增內容 → 重跑子集 → 測量效能」成為固定流程。
5. 移除不再需要的 CDN 標籤
改成自架後,檢查 <head> 裡的 Google Fonts preconnect、preload、stylesheet 與 noscript 標籤,避免新舊兩套字型同時載入。
常見錯誤與取捨
- 直接上傳 OTF/TTF:檔案太大,應先輸出 WOFF2。
- 只留下漢字、忘了 Latin:英文和數字可能換成不同風格,應保留常用 Latin 子集。
- 把整個網站都套品牌字體:流量與載入成本會快速增加,先限制在標題或 UI。
- 以為 fallback 就代表沒有問題:雖然不會 tofu,但罕見字的字形可能不一致,上線前要測試。
- 只看檔案大小、不測實際體驗:請用 PageSpeed Insights 或自己的效能驗收流程觀察 LCP、FCP、CLS 與文字顯示是否改善。
推薦工具/資源
| 工具或做法 | 適合誰 | 特色 |
|---|---|---|
| PageSpeed Insights | 所有站長 | 檢查字型改動是否真的改善載入與 Core Web Vitals |
cn-font-split | 需要 Unicode 分片的網站 | 讓瀏覽器按需載入字型 |
subset-font | 想整合 Node.js build 流程的開發者 | 將站內實際用字打包成 WOFF2 |
| 系統字體堆疊 | 內容站與文件站 | 不需下載,載入成本最低 |
結語:先少載入,再追求一致
- 內容站先用系統字體,不要預設整包載入 CJK Web Font。
- 需要品牌字形時,只把標題或 UI 子集化並自架 WOFF2。
- 用
font-display、fallback 與實際 CWV 測量確認取捨,而不是只看理論檔案大小。
下一步很簡單:先用 PageSpeed Insights 記下改動前的數據,再選一個頁面做字型子集化,最後比較 LCP、CLS 和實際文字顯示。先測一頁就好,不必一口氣把整個網站的字型 pipeline 變成考古現場。

