• Anonymous
Web 效能優化完整指南:字體載入與資源預載入
深入解析現代網站效能優化技術,包含 Google Fonts 非阻塞載入、preconnect、preload 策略,以及實際案例分析
#performance
#optimization
#fonts
#preload
#web-vitals
網站效能直接影響使用者體驗和 SEO 排名。本文將深入探討如何優化字體載入和資源預載入,讓你的網站載入速度提升數十毫秒甚至秒級。
目錄
- 為什麼效能優化很重要
- 字體載入的問題
- CSS @import 的阻塞性問題
- 解決方案:HTML preload
- preconnect 與 dns-prefetch
- 實戰案例:從 @import 遷移到 preload
- 進階優化技巧
- 效能測量與驗證
為什麼效能優化很重要
根據 Google 的研究:
- 53% 的行動用戶會在頁面載入超過 3 秒時離開
- 頁面載入時間每增加 1 秒,轉換率下降 7%
- Core Web Vitals 是 Google 搜尋排名的重要因素
Core Web Vitals 指標
| 指標 | 全名 | 目標值 | 說明 |
|---|---|---|---|
| LCP | Largest Contentful Paint | < 2.5s | 最大內容繪製時間 |
| FID | First Input Delay | < 100ms | 首次輸入延遲 |
| CLS | Cumulative Layout Shift | < 0.1 | 累積版面位移 |
字體載入優化特別影響 LCP 和 CLS 這兩個指標。
字體載入的問題
FOIT vs FOUT
當瀏覽器載入自訂字體時,會有兩種常見的問題:
FOIT (Flash of Invisible Text)
- 文字在字體載入前完全不顯示
- 用戶看到空白區域
- 較差的使用者體驗
FOUT (Flash of Unstyled Text)
- 文字先以系統字體顯示
- 字體載入後切換到自訂字體
- 可能造成版面跳動
Google Fonts 的載入流程
用戶請求頁面
↓
瀏覽器解析 HTML
↓
發現 CSS @import 或 <link>
↓
請求 Google Fonts CSS
↓
解析 CSS,發現多個 @font-face
↓
請求實際字體檔案 (.woff2)
↓
字體載入完成,文字顯示
這個過程涉及多次網路往返,特別是使用 @import 時會造成渲染阻塞。
CSS @import 的阻塞性問題
問題示範
許多開發者習慣在 CSS 中這樣引入 Google Fonts:
/* global.css - 這會造成渲染阻塞! */
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap');
body {
font-family: 'Inter', sans-serif;
}
為什麼這是問題?
- CSS 阻塞渲染:瀏覽器必須等待 CSS 完全載入才能開始渲染
- 巢狀請求:先載入你的 CSS,再發現 @import,再請求 Google Fonts
- 延遲發現:瀏覽器無法提前知道需要載入字體
時間線比較
使用 @import:
HTML ──────────────────────────────────────→
CSS ─────────────────────────────→
Google Fonts CSS ───→
字體開始載入...
▲ 渲染開始
使用 HTML preload:
HTML ──────────────────────────────────────→
CSS ────────────────────────→
Google Fonts CSS ────────────→ (並行)
字體開始載入...
▲ 渲染開始(更早!)
解決方案:HTML preload
基本概念
<link rel="preload"> 告訴瀏覽器:「這個資源很重要,請盡早載入!」
完整實作
<head>
<!-- 1. 預連接到字體伺服器 -->
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<!-- 2. 預載入字體 CSS(非阻塞) -->
<link
rel="preload"
href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;500;600&display=swap"
as="style"
onload="this.onload=null;this.rel='stylesheet'"
/>
<!-- 3. 無 JavaScript 的後備方案 -->
<noscript>
<link
href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;500;600&display=swap"
rel="stylesheet"
/>
</noscript>
</head>
程式碼解析
rel=“preload”
<link rel="preload" href="..." as="style" />
rel="preload":告訴瀏覽器預先載入這個資源as="style":指定資源類型為樣式表- 瀏覽器會優先載入,但不會阻塞渲染
onload 事件
onload="this.onload=null;this.rel='stylesheet'"
- 當資源載入完成時觸發
this.onload=null:防止重複觸發this.rel='stylesheet':將 preload 改為正式的樣式表
noscript 後備
<noscript>
<link href="..." rel="stylesheet" />
</noscript>
- 為禁用 JavaScript 的用戶提供後備方案
- 確保無論如何都能載入字體
preconnect 與 dns-prefetch
preconnect
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
作用:提前建立與第三方伺服器的連接,包括:
- DNS 解析
- TCP 連接
- TLS 協商(HTTPS)
節省時間:約 100-500ms
dns-prefetch(替代方案)
<link rel="dns-prefetch" href="https://fonts.googleapis.com" />
作用:只執行 DNS 解析 適用場景:不確定是否會用到的資源 節省時間:約 20-120ms
crossorigin 屬性
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
- Google Fonts 的字體檔案託管在
fonts.gstatic.com - 字體是跨域資源,需要
crossorigin屬性 - 沒有這個屬性,瀏覽器會建立兩次連接
實戰案例:從 @import 遷移到 preload
修改前(global.css)
/* 阻塞渲染的方式 */
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;500;600&display=swap');
@tailwind base;
@tailwind components;
@tailwind utilities;
修改後(global.css)
/* 字體透過 HTML preload 載入,不再在這裡引入 */
@tailwind base;
@tailwind components;
@tailwind utilities;
修改後(BaseLayout.astro)
---
// Astro 組件程式碼
---
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<!-- 預連接 -->
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<!-- 非阻塞字體載入 -->
<link
rel="preload"
href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;500;600&display=swap"
as="style"
onload="this.onload=null;this.rel='stylesheet'"
/>
<noscript>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet" />
</noscript>
<title>{title}</title>
</head>
<body>
<slot />
</body>
</html>
進階優化技巧
1. 使用 font-display: swap
Google Fonts URL 中的 display=swap 參數:
https://fonts.googleapis.com/css2?family=Inter&display=swap
這會生成以下 CSS:
@font-face {
font-family: 'Inter';
font-display: swap; /* 關鍵! */
src: url(...) format('woff2');
}
font-display 值說明:
| 值 | 行為 |
|---|---|
auto | 瀏覽器預設(通常是 block) |
block | 短暫隱藏文字,等待字體 |
swap | 立即顯示後備字體,載入後切換 |
fallback | 非常短暫隱藏,然後顯示後備 |
optional | 只用快取的字體,否則用後備 |
2. 子集化字體
如果你只需要英文字母:
https://fonts.googleapis.com/css2?family=Inter&subset=latin
如果你需要中文,考慮使用 subset=chinese-traditional。
3. 可變字體(Variable Fonts)
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@100..900&display=swap" rel="stylesheet" />
一個字體檔案包含所有字重,減少請求數量。
4. 本地字體快取
@font-face {
font-family: 'Inter';
src: local('Inter'), /* 先檢查本地 */
url('/fonts/Inter.woff2') format('woff2'),
url('/fonts/Inter.woff') format('woff');
font-display: swap;
}
效能測量與驗證
使用 Lighthouse
# Chrome DevTools → Lighthouse → Performance
# 或使用 CLI
npx lighthouse https://your-site.com --only-categories=performance
使用 WebPageTest
- 前往 webpagetest.org
- 輸入網址
- 查看 Waterfall 圖
檢查項目
- 字體 CSS 是否並行載入
- preconnect 是否生效
- 沒有 FOIT(隱形文字閃爍)
- CLS 分數是否改善
Chrome DevTools 驗證
- 打開 DevTools
- 切換到 Network 面板
- 刷新頁面
- 篩選
font - 查看 Waterfall 圖
理想狀態:字體請求應該在頁面載入初期就開始。
總結
優化清單
- ✅ 使用
preconnect預連接到字體伺服器 - ✅ 使用
preload非阻塞載入字體 CSS - ✅ 從 CSS
@import遷移到 HTML<link> - ✅ 使用
font-display: swap避免 FOIT - ✅ 提供
<noscript>後備方案 - ✅ 只載入需要的字重和子集
預期效果
- LCP 改善 100-500ms
- CLS 降低(更少的版面位移)
- 網站感覺更快、更流暢