Skip to content
Anonymous

Web 效能優化完整指南:字體載入與資源預載入

深入解析現代網站效能優化技術,包含 Google Fonts 非阻塞載入、preconnect、preload 策略,以及實際案例分析

#performance #optimization #fonts #preload #web-vitals

網站效能直接影響使用者體驗和 SEO 排名。本文將深入探討如何優化字體載入和資源預載入,讓你的網站載入速度提升數十毫秒甚至秒級。

目錄

  1. 為什麼效能優化很重要
  2. 字體載入的問題
  3. CSS @import 的阻塞性問題
  4. 解決方案:HTML preload
  5. preconnect 與 dns-prefetch
  6. 實戰案例:從 @import 遷移到 preload
  7. 進階優化技巧
  8. 效能測量與驗證

為什麼效能優化很重要

根據 Google 的研究:

  • 53% 的行動用戶會在頁面載入超過 3 秒時離開
  • 頁面載入時間每增加 1 秒,轉換率下降 7%
  • Core Web Vitals 是 Google 搜尋排名的重要因素

Core Web Vitals 指標

指標全名目標值說明
LCPLargest Contentful Paint< 2.5s最大內容繪製時間
FIDFirst Input Delay< 100ms首次輸入延遲
CLSCumulative Layout Shift< 0.1累積版面位移

字體載入優化特別影響 LCPCLS 這兩個指標。


字體載入的問題

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;
}

為什麼這是問題?

  1. CSS 阻塞渲染:瀏覽器必須等待 CSS 完全載入才能開始渲染
  2. 巢狀請求:先載入你的 CSS,再發現 @import,再請求 Google Fonts
  3. 延遲發現:瀏覽器無法提前知道需要載入字體

時間線比較

使用 @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 />

作用:提前建立與第三方伺服器的連接,包括:

  1. DNS 解析
  2. TCP 連接
  3. 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

  1. 前往 webpagetest.org
  2. 輸入網址
  3. 查看 Waterfall 圖

檢查項目

  • 字體 CSS 是否並行載入
  • preconnect 是否生效
  • 沒有 FOIT(隱形文字閃爍)
  • CLS 分數是否改善

Chrome DevTools 驗證

  1. 打開 DevTools
  2. 切換到 Network 面板
  3. 刷新頁面
  4. 篩選 font
  5. 查看 Waterfall 圖

理想狀態:字體請求應該在頁面載入初期就開始。


總結

優化清單

  1. ✅ 使用 preconnect 預連接到字體伺服器
  2. ✅ 使用 preload 非阻塞載入字體 CSS
  3. ✅ 從 CSS @import 遷移到 HTML <link>
  4. ✅ 使用 font-display: swap 避免 FOIT
  5. ✅ 提供 <noscript> 後備方案
  6. ✅ 只載入需要的字重和子集

預期效果

  • LCP 改善 100-500ms
  • CLS 降低(更少的版面位移)
  • 網站感覺更快、更流暢

參考資源