Skip to content

網頁效能的度量與最佳化

🎯 核心問題

為什麼你的網頁載入很慢,使用者還在瘋狂抱怨卡頓? 這就像是問:為什麼餐廳上菜慢、顧客等得不耐煩?本章將帶你深入理解前端效能最佳化的核心概念,讓你的網頁「飛」起來。


1. 為什麼要「效能最佳化」?

1.1 從能用到好用:效能最佳化的演變

十年前的網頁非常簡單,一個頁面可能就幾 KB,載入速度幾乎感覺不到延遲。那時的我們根本不需要考慮效能最佳化——因為問題還沒出現。

但現在完全不同了。現代網頁的複雜度呈指數級增長:一個電商首頁可能有幾十張高清圖片,一個社群平台可能同時載入上千條動態,一個管理後台可能包含幾十個互動元件。這些「豐富」的功能背後,是龐大的程式碼量和資源體積,如果不好好最佳化,使用者體驗就會一塌糊塗。

👴 十年前的網頁

  • 單個頁面只有幾 KB 到幾十 KB
  • 只有文字和少量圖片
  • 使用者幾乎感覺不到載入延遲
  • 不需要任何效能最佳化

🚀 現代的網頁

  • 單個頁面可能幾 MB 甚至更大
  • 有高清圖片、影片、互動元件
  • 載入慢、捲動卡、點擊反應遲鈍
  • 必須做效能最佳化才能用

這就是「效能最佳化」要解決的問題:讓使用者等待的時間更短,讓操作更流暢。

1.2 一個真實的踩坑故事:為什麼你需要了解效能最佳化

你可能會說:「現在的網路這麼快,裝置這麼好,還需要考慮效能最佳化嗎?」讓我講一個真實的故事,你就會明白為什麼這些知識如此重要。

小王的效能踩坑記

小王是一個剛入職的前端工程師,負責開發公司的電商首頁。他用了最新的 Vue 3、最流行的 UI 函式庫,功能做得非常完善,自己在公司的高效能電腦上測試時一切正常。

但上線後第二天,客服部門就炸鍋了——大量使用者投訴說「網站太卡了」、「圖片載入不出來」、「點擊按鈕半天沒反應」。小王打開自己的開發機測試,一切都很流暢啊,他完全不理解問題出在哪裡。

後來請師傅幫忙定位,師傅讓他用一台普通的筆記型電腦,連上普通的 4G 行動網路,然後再測試自己的網站。小王這才傻眼了:首頁載入要等十幾秒,捲動列表時卡得像 PPT,點擊按鈕後要等好幾秒才有反應。

原來小王的開發環境是頂配的 MacBook Pro + 千兆光纖,而大多數使用者用的是普通裝置 + 行動網路。他寫的程式碼裡有幾十張未壓縮的高清圖片,引入了整個 UI 函式庫但只用了幾個元件,還在渲染時做了大量同步計算。

解決方案其實不複雜:壓縮圖片、按需引入元件、把計算放到背景執行緒、使用虛擬列表。這樣改動之後,首頁載入時間從十幾秒變成了 2 秒,捲動也非常流暢,使用者投訴立刻消失了。

小王從此明白了一個道理:不了解效能最佳化,你寫出來的程式碼在自己電腦上跑得飛快,但在使用者裝置上可能根本沒法用。

💡 核心啟示

效能最佳化不是可選項,而是必備技能。你要站在使用者的視角思考問題——他們用的是普通裝置、普通網路,如果你的程式碼在他們裝置上跑不動,那就說明你需要最佳化了。


2. 核心概念:載入、渲染、互動

🤔 這些概念和效能有什麼關係?

載入、渲染、互動就是使用者存取網頁的三個核心環節,每個環節都可能成為效能瓶頸。

當使用者存取你的網頁時,會依次經歷:

  1. 載入 → 把 HTML/CSS/JS/圖片 從伺服器下載到瀏覽器
  2. 渲染 → 把下載的內容「畫」成使用者能看到的頁面
  3. 互動 → 回應使用者的點擊、捲動等操作

所以,效能最佳化就是讓這三個環節都快起來。理解它們,你才能知道效能瓶頸出在哪裡,該用什麼方法最佳化。

在深入學習具體最佳化技巧之前,我們需要先搞清楚這幾個核心概念。為了幫助你更好地理解,我們用餐廳的比喻來類比它們之間的關係。

2.1 用餐廳比喻理解三個環節

想像你去一家餐廳吃飯,這個過程和存取網頁驚人地相似:

環節🍽️ 餐廳比喻實際作用具體例子
載入把食材從倉庫運送到廚房把 HTML/CSS/JS/圖片 從伺服器下載到瀏覽器使用者打開網頁,瀏覽器開始下載各種資源
渲染廚師把食材加工成菜餚瀏覽器把程式碼轉換成使用者能看到的頁面瀏覽器解析 HTML、計算佈局、繪製頁面
互動服務生回應顧客的需求瀏覽器回應點擊、捲動等操作使用者點擊按鈕,頁面做出回饋

2.2 載入(Loading):食材運送

載入是指把網頁所需的各種資源(HTML、CSS、JavaScript、圖片、字型等)從伺服器下載到瀏覽器的過程。這個過程就像把食材從倉庫運送到廚房,如果運送慢或者食材太多,廚房就得乾等著。

為什麼載入會慢? 主要有三個原因:首先,資源體積太大——一張未壓縮的高清圖片可能就有 5MB,相當於下載一本小說;其次,網路延遲——如果伺服器在國外,或者使用者用行動網路,每個請求都要等很久;最後,請求太多——瀏覽器同時下載的資源數量有限,太多資源就要排隊。

🔍 看看載入階段都做了什麼

當使用者在瀏覽器網址列輸入網址並按下 Enter 後,會依次發生:

  1. DNS 解析:把網域名稱(如 www.example.com)轉換成 IP 位址(如 192.168.1.1),就像透過電話簿查詢餐廳地址
  2. TCP 連線:瀏覽器和伺服器建立連線,就像打電話前要先撥號
  3. TLS 握手:建立安全連線(HTTPS),就像確認對方身分
  4. 請求資源:瀏覽器向伺服器請求 HTML 檔案
  5. 解析 HTML:瀏覽器解析 HTML,發現需要 CSS、JS、圖片等資源,繼續請求
  6. 下載資源:把所有需要的資源下載到本地
  7. 開始渲染:下載完成後,開始渲染頁面

前面的 1-4 步叫「首字節時間」(TTFB),後面的 5-7 步是真正的資源下載時間。

常見的載入最佳化手段:

  • 壓縮資源:把檔案變小(Gzip、Brotli 壓縮)
  • 使用 CDN:把檔案存在離使用者更近的伺服器上
  • 延遲載入:只載入使用者看得到的內容,剩下的等使用者捲動時再載入
  • 程式碼分割:把大檔案拆成小檔案,按需載入

2.3 渲染(Rendering):廚師做菜

渲染是指瀏覽器把下載的 HTML、CSS、JavaScript 轉換成使用者能看到的頁面的過程。這個過程就像廚師把食材加工成菜餚,如果工序複雜、步驟多,上菜就會慢。

📖 什麼是「渲染」?

你可能聽說過「渲染」這個詞,它到底是什麼?

簡單來說,渲染就是把程式碼變成畫面的過程。

瀏覽器要做的事情包括:

  1. 解析 HTML → 生成 DOM 樹(頁面的結構)
  2. 解析 CSS → 生成 CSSOM 樹(頁面的樣式)
  3. 合併 → 生成渲染樹(結構和樣式的結合)
  4. 佈局 → 計算每個元素的位置和大小
  5. 繪製 → 把元素畫出來
  6. 合成 → 把多個圖層合併成最終畫面

這個過程非常複雜,任何一個環節出問題,都會導致頁面卡頓。

為什麼渲染會慢? 主要有兩個原因:首先,頁面太複雜——如果一個頁面有上萬個 DOM 節點,瀏覽器計算佈局和繪製就會非常耗時;其次,頻繁修改頁面——如果 JavaScript 程式碼頻繁修改 DOM,會導致瀏覽器反覆重新佈局和繪製,消耗大量效能。

📁 看看渲染階段都做了什麼

渲染的完整流程

HTML (字串)

[解析 HTML] → 生成 DOM 樹

DOM 樹 (頁面結構)

CSS (樣式表)

[解析 CSS] → 生成 CSSOM 樹

CSSOM 樹 (頁面樣式)

DOM 樹 + CSSOM 樹

[合併] → 生成渲染樹

渲染樹 (要渲染的元素)

[佈局 Layout] → 計算每個元素的位置和大小

[繪製 Paint] → 填充顏色、繪製文字

[合成 Composite] → 合併多個圖層

最終畫面

關鍵渲染路徑(Critical Rendering Path):瀏覽器要盡快把第一屏內容渲染出來,讓使用者覺得「網站很快」。這叫「關鍵渲染路徑最佳化」。

👇 動手看看: 下面這個演示展示了瀏覽器是如何渲染頁面的。點擊「下一步」,觀察渲染的各個階段:

Frontend Performance Optimization Map
Select a dimension to explore bottlenecks and matching optimization techniques.

⚠️ Common Bottlenecks

  • Large assets
    Images and JS bundles are not compressed, so downloads take longer.
  • Too many requests
    HTTP/1.1 head-of-line blocking makes resources wait in line.
  • Network latency
    The server is physically far away, increasing RTT.
How to fix it?

🚀 Solutions

  • Asset compression
    Use Gzip/Brotli and image formats such as WebP.
    Smaller size
  • Lazy loading
    Load only resources visible in the current viewport.
    Smaller sizeFewer requests
  • CDN acceleration
    Distribute assets to nodes close to users.
    Shorter distance
  • HTTP caching
    Use browser cache to avoid repeated requests.
    Fewer requests

Core goal: Deliver resources to the browser faster by reducing size, request count, and distance.

常見的渲染最佳化手段:

  • 減少重排和重繪:避免頻繁修改 DOM,使用 transformopacity 代替 topwidth
  • 虛擬列表:只渲染可見區域的內容,大量資料時效能提升明顯
  • CSS 動畫:用 CSS 動畫代替 JavaScript 動畫,效能更好

2.4 互動(Interaction):服務生回應

互動是指瀏覽器回應使用者操作(點擊、捲動、輸入等)的過程。這個過程就像服務生回應顧客的需求,如果服務生忙不過來,顧客就得等。

為什麼互動會卡? 主要原因是主執行緒被阻塞了。瀏覽器的 JavaScript 是單執行緒的,如果程式碼在執行複雜的計算,就沒法回應使用者的操作,導致頁面卡頓。

🤔 什麼是「主執行緒」?

瀏覽器有多個執行緒,但負責執行 JavaScript、渲染頁面、回應使用者操作的只有一個——主執行緒

你可以把主執行緒想像成一個忙碌的服務生,他要做很多事情:

  • 執行 JavaScript 程式碼(計算資料、呼叫 API)
  • 渲染頁面(佈局、繪製)
  • 回應使用者操作(點擊按鈕、捲動頁面)

問題來了:他只有一個人。如果他在執行複雜的 JavaScript 計算(比如處理一萬條資料),這時候使用者點擊了按鈕,他是沒法立即回應的,必須等計算完才行。這就是卡頓的根源。

解決方案

  • 把複雜的計算放到 Web Worker(背景執行緒)
  • 使用時間切片,把大任務拆成小任務
  • 避免同步的複雜操作,改用非同步

👇 動手試試看: 下面這個演示對比了同步計算和 Web Worker 的區別。點擊「開始計算」,觀察頁面是否卡頓:

📊Core Web VitalsAdjust load time and observe how performance metrics change.
FCP
First Contentful Paint
0.8 s
First contentful paint
Good
LCP
Largest Contentful Paint
1.8 s
Largest contentful paint
Good
FID
First Input Delay
200 ms
First input delay
Needs improvement
CLS
Cumulative Layout Shift
0.15
Cumulative layout shift
Needs improvement
Good
Needs improvement
Poor
💡Core metrics:FCP ≤ 1.8s, LCP ≤ 2.5s, FID ≤ 100ms, and CLS ≤ 0.1. The goal is to keep every metric in the "Good" range.

常見的互動最佳化手段:

  • 防抖和節流:限制事件的觸發頻率(比如捲動事件、輸入事件)
  • Web Worker:把複雜計算放到背景執行緒,不阻塞主執行緒
  • 時間切片:把大任務拆成小任務,讓瀏覽器有機會回應使用者操作

3. 實戰:一個團隊的效能最佳化演進之路

講了這麼多概念,讓我們看一個真實的案例:某新創公司是如何從「完全沒考慮效能」一步步進化到「系統化效能最佳化」的。透過這個案例,你會更直觀地理解效能最佳化到底解決了什麼問題。

3.1 演進的全景圖

下面這張表展示了效能最佳化的四個階段,你可以看到最佳化手段、工具、指標是如何一步步進化的:

階段最佳化手段監控工具核心指標核心變化
階段一:原始時代無(沒考慮)無(憑感覺)完全沒效能意識,能跑就行
階段二:手動最佳化壓縮圖片、減少請求瀏覽器 Network 面板頁面載入時間開始有意識,但方法原始
階段三:系統化最佳化程式碼分割、延遲載入、虛擬列表Lighthouse、Performance 面板FCP、LCP、TBT用專業工具,有明確的最佳化目標
階段四:持續最佳化效能預算、CI/CD 檢查RUM、Lighthouse CIINP、CLS、全鏈路監控把效能納入開發流程

📊 從表格中你能看到什麼?

讓我們逐行解讀這張表:

階段一 → 階段二:從「沒意識」到「有意識」。這是關鍵的一步——開發者開始意識到效能是個問題,並且嘗試最佳化。但最佳化手段比較原始,主要靠感覺和經驗。

階段二 → 階段三:從「手動」到「系統化」。這是質的飛躍——開始使用專業工具(Lighthouse、Performance 面板)來診斷效能問題,用科學的方法(程式碼分割、延遲載入)來最佳化,而不是憑感覺。

階段三 → 階段四:從「一次性最佳化」到「持續最佳化」。當效能最佳化成為開發流程的一部分後,就需要建立監控體系(RUM、真實使用者監控),在開發階段就設定效能預算,防止退化。

總結一下:效能最佳化演進不只是「用了更多技術」,而是整個思維方式的升級——從被動回應到主動預防,從憑感覺到資料驅動,從單次最佳化到持續改進。

3.2 階段一:原始時代——完全沒考慮

為什麼叫「原始時代」?因為這個階段完全沒考慮效能問題——能跑就行。團隊只有 3 個人,做一個簡單的企業官網,專案很小,看起來沒什麼問題。

但隨著專案變大、使用者增多,問題開始暴露出來。

開發方式

  • 最佳化手段:無,直接開發,沒考慮效能
  • 監控工具:無,憑感覺判斷快慢
  • 核心指標:無

這個階段的特點

  • 優點:開發快,沒有額外的學習成本
  • 缺點:使用者體驗差,網速慢時根本沒法用
查看當時的問題

遇到的具體問題

  1. 圖片太大:產品經理上傳了一張 5MB 的首頁 Banner 圖,行動網路使用者打開網頁要等 1 分鐘
  2. 沒有壓縮:CSS 和 JS 檔案完全沒有壓縮,體積是壓縮後的 3 倍
  3. 沒有快取:每次存取都要重新下載所有資源,老使用者也要等
  4. 同步載入:所有 JS 檔案都在 <head> 中同步載入,阻塞頁面渲染

使用者的回饋

  • 「你們網站怎麼打不開?」
  • 「圖片半天載入不出來,就是空白」
  • 「點擊按鈕沒反應,是不是網站壞了?」

當時的臨時解決方案

html
<!-- 用 loading 遮罩「欺騙」使用者 -->
<div id="loading">載入中...</div>
<script>
  // 頁面載入完成後才移除遮罩
  window.onload = function() {
    document.getElementById('loading').style.display = 'none'
  }
</script>

這完全是在「自欺欺人」——頁面還是很慢,只是使用者看不到而已。

3.3 階段二:手動最佳化——開始有意識

原始時代的問題累積到一定程度,團隊終於決定開始做效能最佳化。這是一個重要的轉折點——從「完全不考慮」到「有意識地最佳化」。

但這個階段的最佳化比較原始,主要靠壓縮圖片、合併檔案等簡單手段。

開發方式

  • 最佳化手段:手動壓縮圖片、合併 CSS/JS 檔案、減少 HTTP 請求
  • 監控工具:瀏覽器 Network 面板、簡單的計時日誌
  • 核心指標:頁面載入時間(手動用碼錶計時)

這個階段的特點

  • 優點:有明顯改善,使用者不再瘋狂投訴
  • 缺點:最佳化不系統,容易反覆,缺少量化指標
查看手動最佳化的具體做法

手動最佳化手段

  1. 手動壓縮圖片

    • 用 Photoshop 把每張圖片手動「另存為 Web 格式」
    • 把 PNG 轉 JPEG(有損壓縮,但體積小很多)
    • 縮小圖片尺寸(比如 2000px 寬的圖縮小到 800px)
  2. 手動合併檔案

    html
    <!-- 最佳化前:10 個 JS 檔案 = 10 個請求 -->
    <script src="utils.js"></script>
    <script src="api.js"></script>
    <script src="component-a.js"></script>
    <script src="component-b.js"></script>
    ...(還有 6 個)
    
    <!-- 最佳化後:1 個合併的 JS 檔案 = 1 個請求 -->
    <script src="all.js"></script>
  3. 把 CSS/JS 移到頁面底部

    html
    <body>
      <!-- 頁面內容 -->
      <h1>歡迎訪問</h1>
    
      <!-- 最佳化:把 CSS/JS 放在最後 -->
      <link rel="stylesheet" href="style.css">
      <script src="app.js"></script>
    </body>

帶來的改善

  • 圖片體積從 5MB 減小到 500KB(減少 90%)
  • HTTP 請求數從 30 個減少到 5 個
  • 頁面載入時間從 30 秒減少到 8 秒

新的痛點

  1. 手動工作量大:每次更新都要手動壓縮圖片、合併檔案
  2. 容易忘記:新人不知道要最佳化,直接上傳原圖
  3. 缺少量化:只知道「快了一些」,但不知道具體快多少

3.4 階段三:系統化最佳化——用工具和資料說話

階段二的問題(手動工作量大、缺少量化)困擾了團隊很久。直到後來,團隊發現了 Lighthouse、Performance 面板等專業工具,進入了系統化最佳化時代。

這個階段的核心是用資料驅動最佳化——先用工具診斷問題,找到效能瓶頸,再有針對性地最佳化。

開發方式

  • 最佳化手段:程式碼分割、延遲載入、虛擬列表、圖片自動壓縮
  • 監控工具:Lighthouse、Chrome Performance 面板、WebPageTest
  • 核心指標:FCP(首屏時間)、LCP(最大內容繪製)、TBT(總阻塞時間)
系統化最佳化的具體做法

使用 Lighthouse 診斷問題

Lighthouse 是 Google 開發的自動化效能測試工具,可以給出全面的效能報告和最佳化建議。

bash
# 使用 Lighthouse 測試網頁
lighthouse https://www.example.com --view

Lighthouse 會給出:

  • 效能評分(0-100 分)
  • 核心指標(FCP、LCP、CLS、TBT、INP)
  • 最佳化建議(比如「啟用文字壓縮」、「移除未使用的 JavaScript」)

關鍵指標解讀

指標全稱含義理想值
FCPFirst Contentful Paint首次內容繪製時間(使用者看到第一塊內容的時間)<1.8s
LCPLargest Contentful Paint最大內容繪製時間(主要內容載入完成的時間)<2.5s
TBTTotal Blocking Time總阻塞時間(主執行緒被阻塞的總時間)<200ms
CLSCumulative Layout Shift累積佈局偏移(頁面元素亂跳的程度)<0.1

這個階段的特點

  • 優點:最佳化有針對性,效果好,有量化指標
  • 缺點:需要學習工具和指標,有一定門檻
查看系統化最佳化的具體技術

1. 程式碼分割(Code Splitting)

把大檔案拆成小檔案,按需載入。比如使用者訪問首頁時,只載入首頁需要的程式碼,等到點擊「關於我們」時,再去載入關於頁面的程式碼。

js
// 最佳化前:所有程式碼都在一個檔案,一次性載入
import About from './views/About.vue'
import Contact from './views/Contact.vue'
// ... 還有 10 個頁面

// 最佳化後:延遲載入,訪問時才載入
const About = () => import('./views/About.vue')
const Contact = () => import('./views/Contact.vue')

效果:首頁載入的程式碼量減少 70%,首屏時間從 5 秒降到 1.5 秒。

2. 圖片延遲載入(Lazy Loading)

只載入使用者看得到的圖片,捲動到可視區域時再載入其他圖片。

html
<!-- 現代瀏覽器支援原生的延遲載入 -->
<img src="placeholder.jpg" data-src="real-image.jpg" loading="lazy" />

效果:首頁載入的圖片數量從 20 張減少到 3 張,節省 80% 的頻寬。

3. 虛擬列表(Virtual Scrolling)

如果要渲染 10,000 條資料,不要真的建立 10,000 個 DOM 節點,而是只渲染可見區域的 20 條,捲動時動態替換。

vue
<!-- 使用 vue-virtual-scroller 元件 -->
<RecycleScroller
  :items="items"
  :item-size="50"
  key-field="id"
>
  <template #default="{ item }">
    <div>{{ item.name }}</div>
  </template>
</RecycleScroller>

效果:10,000 條資料從「卡死」變成「流暢捲動」,記憶體佔用減少 95%。

3.5 階段四:持續最佳化——把效能納入開發流程

當工具和方法成熟後,團隊開始關注更深層次的問題:如何防止效能退化?如何讓效能成為開發流程的一部分?

這個階段的核心是建立效能監控和預算體系——不是上線後再最佳化,而是在開發階段就預防效能問題。

開發方式

  • 最佳化手段:效能預算(Performance Budget)、Lighthouse CI、真實使用者監控(RUM)
  • 監控工具:Lighthouse CI、WebPageTest API、Google Analytics
  • 核心指標:INP(互動延遲)、CLS(佈局偏移)、全鏈路監控
持續最佳化的具體做法

1. 設定效能預算

在打包配置中設定限制,超過就報錯,防止「無意中引入大檔案」。

js
// vite.config.js
export default defineConfig({
  build: {
    rollupOptions: {
      output: {
        // 限制單個檔案不超過 200KB
        chunkFileNames: 'js/[name]-[hash].js',
      }
    },
    // 超過 200KB 時發出警告
    chunkSizeWarningLimit: 200
  }
})

2. Lighthouse CI

每次提交程式碼時,自動執行 Lighthouse 測試,如果效能分數下降,就阻止合併。

yaml
# .github/workflows/lighthouse.yml
name: Lighthouse CI
on: [pull_request]
jobs:
  lighthouse:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - name: Run Lighthouse CI
        uses: treosh/lighthouse-ci-action@v9
        with:
          urls: |
            https://staging.example.com
          budgetPath: ./budget.json

3. 真實使用者監控(RUM)

在真實使用者瀏覽器中收集效能資料,而不是只在開發環境測試。

js
// 傳送效能資料到伺服器
const perfData = performance.getEntriesByType('navigation')[0]
const lcp = performance.getEntriesByType('largest-contentful-paint')[0]

fetch('/api/perf', {
  method: 'POST',
  body: JSON.stringify({
    fcp: perfData.loadEventEnd - perfData.fetchStart,
    lcp: lcp.renderTime || lcp.loadTime,
    url: window.location.href
  })
})

效果

  • 能及時發現效能退化(比如某次提交導致 LCP 從 2 秒變成 5 秒)
  • 能了解真實使用者的體驗(而不是開發環境的「理想狀態」)
  • 能針對性地最佳化最慢的那 10% 使用者

這個階段會做什麼?

  1. 效能預算:限制檔案大小、請求數量,超過就報警
  2. CI/CD 檢查:每次提交程式碼自動測試效能,退化就阻止合併
  3. 真實使用者監控:收集真實使用者的效能資料,持續改進
  4. 定期效能報告:每週/每月生成效能報告,追蹤趨勢

4. 常見效能瓶頸與解決方案

講了這麼多理論,讓我們看看實際開發中最常見的效能問題,以及如何解決。

4.1 圖片載入慢

問題表現:圖片半天載入不出來,或者載入過程中頁面跳動。

原因

  • 圖片體積太大(高清原圖)
  • 圖片尺寸太大(2000px 寬的圖顯示為 200px)
  • 沒有延遲載入(一次性載入所有圖片)

解決方案

  1. 使用現代圖片格式(WebP、AVIF):
html
<!-- 現代:WebP 格式,體積小 30-70% -->
<picture>
  <source srcset="image.webp" type="image/webp">
  <img src="image.jpg" alt="圖片">
</picture>
  1. 響應式圖片(根據裝置大小載入不同尺寸):
html
<!-- 小裝置載入小圖,大裝置載入大圖 -->
<img
  src="image-800.jpg"
  srcset="image-400.jpg 400w,
          image-800.jpg 800w,
          image-1200.jpg 1200w"
  sizes="(max-width: 600px) 400px,
         (max-width: 1200px) 800px,
         1200px"
  alt="響應式圖片">
  1. 延遲載入(使用者捲動到時再載入):
html
<!-- 現代:原生延遲載入 -->
<img src="placeholder.jpg" data-src="real-image.jpg" loading="lazy" />

👇 動手試試看: 下面這個演示對比了延遲載入和不延遲載入的區別。觀察網路請求:

Image Format Comparison: Size and Quality Tradeoffs
Compare size and quality across image formats.
JPEG
Classic
🖼️
500 KB
File size500 KB
Compression70%
Quality
Browser support100%
Use case
Photos and complex images
PNG
Lossless
🖼️
1.2 MB
File size1.2 MB
Compression40%
Quality
Browser support100%
Use case
Transparent images and icons
WebP
🖼️
250 KB
File size250 KB
Compression85%
Quality
Browser support95%
Use case
Most scenarios
AVIF
Newest
🖼️
180 KB
File size180 KB
Compression90%
Quality
Browser support75%
Use case
Maximum performance

Detailed Comparison

FormatSizeQualityTransparencyAnimationRecommendation
JPEGMediumGood
★★★★☆
PNGLargePerfect
★★★★★
WebPSmallExcellent
★★★★★
AVIFSmallestOutstanding
★★★★★
💡

Optimization Tips

  • Prefer WebP to reduce size by 30-50%.
  • Provide JPEG/PNG fallbacks for older browsers.
  • Use the <picture> element for automatic fallback.
  • Use JPEG for photos and PNG or SVG for icons.
🔧

Recommended Tools

  • Squoosh: open-source image compression from Google.
  • ImageOptim: image optimization tool for macOS.
  • TinyPNG: online smart compression with WebP support.
  • Sharp: Node.js image processing library for automation.

4.2 首屏載入慢

問題表現:使用者打開網頁,白屏時間很長。

原因

  • 載入了太多不必要的程式碼
  • 關鍵渲染路徑被阻塞
  • 沒有做程式碼分割

解決方案

  1. 程式碼分割(Code Splitting):
js
// 路由延遲載入:訪問時才載入
const routes = [
  {
    path: '/about',
    component: () => import('./views/About.vue')  // 訪問 /about 時才載入
  }
]
  1. 預載入關鍵資源(Preload):
html
<!-- 提前告知瀏覽器:這些資源很重要,優先載入 -->
<link rel="preload" href="critical.css" as="style">
<link rel="preload" href="hero-image.jpg" as="image">
  1. 內聯關鍵 CSS
html
<!-- 把首屏需要的 CSS 直接內嵌在 HTML 中 -->
<style>
  /* 首屏關鍵樣式 */
  .hero { background: #000; color: #fff; }
</style>

4.3 捲動卡頓

問題表現:頁面捲動時一卡一卡的,不流暢。

原因

  • 渲染了太多 DOM 節點(比如 10,000 條資料)
  • 捲動事件監聽器中有複雜計算
  • 頻繁觸發佈局計算

解決方案

  1. 虛擬列表(Virtual Scrolling):
vue
<!-- 只渲染可見區域的內容 -->
<RecycleScroller
  :items="10000"
  :item-size="50"
>
  <template #default="{ item }">
    <div>{{ item.name }}</div>
  </template>
</RecycleScroller>

👇 動手看看: 下面這個演示對比了普通列表和虛擬列表的效能差異:

📜Virtual ScrollingRender only the list items visible in the viewport.
Total items
10,000
Actually rendered
8
Memory saved
~99.9%
1Item #1 - virtual scrolling list item content
2Item #2 - virtual scrolling list item content
3Item #3 - virtual scrolling list item content
4Item #4 - virtual scrolling list item content
5Item #5 - virtual scrolling list item content
6Item #6 - virtual scrolling list item content
7Item #7 - virtual scrolling list item content
8Item #8 - virtual scrolling list item content
💡How it works: Instead of rendering all 10000 items, it renders only the visible viewport items plus a small buffer. On scroll, it calculates which items should appear and uses absolute positioning to create the illusion of a full list. Performance improves from O(n) to O(1).
  1. 節流捲動事件(Throttle):
js
// 限制捲動事件的觸發頻率(最多每 100ms 觸發一次)
const throttledScroll = throttle(() => {
  updatePosition()
}, 100)

window.addEventListener('scroll', throttledScroll)
  1. 使用 CSS will-change
css
/* 提前告知瀏覽器:這個元素會變化,請做好準備 */
.scroll-container {
  will-change: transform;
}

4.4 點擊反應慢

問題表現:點擊按鈕後,要等好幾秒才有反應。

原因

  • 點擊事件處理器中有複雜計算(阻塞主執行緒)
  • 沒有使用防抖(使用者快速點擊多次,觸發多次計算)

解決方案

  1. 防抖點擊事件(Debounce):
js
// 使用者停止點擊 300ms 後才執行
const debouncedClick = debounce(() => {
  submitForm()
}, 300)

button.addEventListener('click', debouncedClick)
  1. 使用 Web Worker(把計算放到背景執行緒):
js
// 主執行緒
const worker = new Worker('calculator.js')
button.addEventListener('click', () => {
  worker.postMessage({ data: largeData })
})

worker.onmessage = (e) => {
  // 計算完成,顯示結果
  showResult(e.data.result)
}

// calculator.js (Worker 執行緒)
self.onmessage = (e) => {
  const result = heavyCalculation(e.data.data)
  self.postMessage({ result })
}

5. 效能監控工具

效能最佳化不是一次性工作,需要持續監控。下面介紹常用的工具。

5.1 瀏覽器開發者工具

Chrome DevTools 是最常用的效能分析工具:

  • Network 面板:查看資源載入情況
  • Performance 面板:分析執行時效能(FPS、主執行緒活動)
  • Lighthouse:一鍵生成效能報告

如何使用 Performance 面板

  1. 打開 Chrome DevTools(F12)
  2. 切換到 Performance 面板
  3. 點擊「Record」按鈕
  4. 操作網頁(捲動、點擊等)
  5. 點擊「Stop」停止錄製
  6. 分析結果:看 FPS(幀率)、主執行緒活動、長任務等

5.2 Lighthouse

Lighthouse 是 Google 開發的自動化效能測試工具:

bash
# 命令列使用
lighthouse https://www.example.com --view

# 或者在 Chrome DevTools 中使用
# 打開 DevTools → Lighthouse → 點擊 "Analyze page load"

Lighthouse 會給出:

  • 效能評分(0-100 分)
  • 核心指標(FCP、LCP、CLS、TBT、INP)
  • 最佳化建議(按影響排序)

5.3 WebPageTest

WebPageTest 是線上效能測試工具,可以從多個地點、多種裝置測試:

bash
# 訪問 https://www.webpagetest.org
# 輸入網址,選擇測試地點和裝置,點擊 "Start Test"

WebPageTest 會給出:

  • 瀑布圖(Waterfall):每個資源載入的時間線
  • 影片對比:最佳化前後的載入過程影片
  • 最佳化建議

6. 效能最佳化清單

下面是一個實用的效能最佳化清單,你可以按照這個順序最佳化你的網頁:

6.1 載入最佳化

  • 壓縮圖片:使用 WebP 格式,壓縮品質 80-85%
  • 響應式圖片:根據裝置大小載入不同尺寸的圖片
  • 延遲載入:圖片和元件延遲載入,只載入可見內容
  • 程式碼分割:按路由分割程式碼,按需載入
  • 壓縮程式碼:啟用 Gzip/Brotli 壓縮
  • 使用 CDN:把靜態資源放到 CDN,加速下載
  • 預載入關鍵資源:使用 <link rel="preload">

6.2 渲染最佳化

  • 減少重排重繪:使用 transformopacity 代替 topwidth
  • 虛擬列表:大量資料時使用虛擬捲動
  • CSS 動畫:優先使用 CSS 動畫,而不是 JavaScript 動畫
  • 最佳化關鍵渲染路徑:內聯關鍵 CSS,延遲載入非關鍵 CSS
  • 避免 @import@import 會阻塞渲染,改用 <link>

6.3 互動最佳化

  • 防抖和節流:捲動、輸入、resize 事件使用防抖/節流
  • Web Worker:複雜計算放到背景執行緒
  • 時間切片:大任務拆成小任務,避免長任務
  • 避免同步佈局:不要在迴圈中讀取佈局屬性(如 offsetHeight

6.4 快取最佳化

  • HTTP 快取:配置 Cache-Control 和 ETag
  • Service Worker:快取靜態資源,實現離線訪問
  • LocalStorage:快取 API 資料,減少請求
  • 記憶體快取:使用 Map/Object 快取計算結果

6.5 監控最佳化

  • Lighthouse CI:每次提交程式碼自動測試效能
  • 真實使用者監控:收集真實使用者的效能資料
  • 效能預算:設定檔案大小限制,超過報警
  • 定期效能報告:每週/每月生成效能趨勢報告

7. 總結

讓我們用一張表格來回顧前端效能最佳化的核心概念:

概念一句話解釋解決的問題常用手段
載入最佳化讓資源下載更快首屏慢、等待時間長壓縮圖片、CDN、程式碼分割、延遲載入
渲染最佳化讓頁面「畫」得更快捲動卡、點擊慢虛擬列表、減少重排重繪、CSS 動畫
互動最佳化讓回應更快點擊沒反應、操作卡頓防抖節流、Web Worker、時間切片
快取最佳化避免重複下載重複訪問慢HTTP 快取、Service Worker、LocalStorage
監控最佳化持續發現問題效能退化Lighthouse、RUM、效能預算

寫在最後

效能最佳化是一個持續演進的話題,工具會變,但核心理念不變:站在使用者的角度思考問題,讓等待時間更短、讓操作更流暢

理解了這些基本原理,無論技術如何更新換代,你都能快速上手、從容應對。

希望這篇文章能幫助你建立起對前端效能最佳化的整體認知。當你在實際專案中遇到效能問題時,能夠知道從哪裡入手、如何定位、怎樣解決。