跳至主要內容
Skip to content

Vue 性能優化總覽

「Vue 性能優化」是一個很大的題目,不能只背一串技巧。面試官真正想看的通常是:你能不能先定位瓶頸,再選對策略,最後說出取捨與驗證方式。

一句話回答:Vue 性能優化要先量測,再從首屏載入、資源體積、渲染成本、響應式成本、資料請求、快取策略、使用者體驗與渲染模式分層處理;不要一上來就套 SSR、虛擬列表或打包優化,而是根據瓶頸選方案。


一、 必考觀念

1. 性能優化不是技巧清單

很多面試回答會變成這樣:

  • 路由懶載入。
  • Tree Shaking。
  • gzip。
  • CDN。
  • v-ifv-show
  • key
  • 防抖節流。
  • 骨架屏。
  • SSR。

這些都對,但如果只是羅列,答案會很散。

比較好的主線是:

text
量測問題
-> 定位瓶頸
-> 分層優化
-> 驗證結果
-> 說出副作用

這個順序比「我知道很多優化手段」更像真實工程經驗。

2. 先判斷慢在哪一層

Vue 應用變慢,可能慢在不同地方:

層級常見問題代表策略
網路層TTFB 高、資源下載慢、快取沒命中CDN、HTTP cache、gzip/brotli、SSR cache
資源層JS/CSS/圖片太大分包、按需載入、圖片格式、字體優化
執行層JS parse/execute 太久、long task延後初始化、Web Worker、拆分重計算
資料層API 串行、重複請求、首屏阻塞並行請求、快取、預取、取消請求
渲染層DOM 太多、Diff 成本高、Layout/Paint 重虛擬列表、穩定 key、讀寫分離、降低更新範圍
響應式層深層 reactive 太大、watch 過多、computed 重算shallowRefmarkRaw、拆分狀態、避免 deep watch
體驗層白屏久、布局跳動、互動卡骨架屏、分階段渲染、樂觀更新、避免 CLS
架構層CSR 首屏與 SEO 不足SSR、SSG、預渲染、邊緣渲染

面試時可以先丟出這張分層圖,接著再展開你最熟的幾層。

3. 先量測,不要憑感覺優化

常用工具:

  • Chrome DevTools Network:看資源大小、請求順序、快取命中。
  • Chrome DevTools Performance:看 long task、JS 執行、Layout、Paint。
  • Lighthouse:看 FCP、LCP、INP、CLS 等指標。
  • Web Vitals / RUM:看真實使用者環境下的數據。
  • Bundle Analyzer:看主包、chunk 與第三方依賴體積。

如果沒有量測,很多優化都只是「看起來很努力」。

例如:

  • 主包 3MB:優先看分包、依賴與壓縮。
  • API 首包 2 秒:優先看資料請求、快取與後端。
  • 首屏一次渲染 5000 個節點:優先看渲染策略。
  • 點擊後卡住 800ms:優先看 long task、computed/watch、同步重計算。

二、 面試回答框架

1. 用四段式回答

回答「Vue 專案如何做性能優化」時,可以這樣組織:

text
第一段:先量測與定位
第二段:依層級說常見優化方向
第三段:補 Vue 特有的渲染與響應式成本
第四段:說取捨、驗證與適用場景

一個完整回答可以是:

我會先用 Network、Performance、Lighthouse 和 bundle analyzer 找瓶頸,而不是直接套技巧。如果是首屏慢,會看主包體積、路由懶載入、圖片字體、API 並行與快取;如果是互動卡,會看 long task、DOM 節點數、Diff 範圍、computed/watch 重算與響應式資料是否過大。Vue 層面會注意穩定 key、避免同時使用 v-ifv-for、大型列表用虛擬列表、重型組件按需載入、狀態拆分、必要時使用 shallowRefmarkRaw。最後會用同樣指標驗證結果,並評估拆包、快取、SSR、骨架屏等方案的副作用。

這個回答的好處是:有流程、有分類、有 Vue 特性,也有資深取捨。

2. 不同場景要有不同答案

性能優化沒有固定套餐。

場景優先思路
首屏白屏久分包、壓縮、圖片、API、骨架屏、SSR/SSG
後台列表卡虛擬列表、分頁、穩定 key、減少欄位與 watcher
表單輸入卡拆分組件、降低父層更新、節流驗證、避免深層 watch
圖表頁慢圖表庫按需載入、延後初始化、資料抽樣、Web Worker
Modal 首次打開慢非首屏組件懶載入、預載高機率模組
SEO 與公開內容頁SSR、SSG、預渲染、head 管理、快取

所以面試時最好不要只說「我會加 SSR」。SSR 是架構選型,不是通用性能開關。


三、 優化主題地圖

這個專題後面可以拆成多篇文章,每篇處理一個方向。

主題要回答的問題狀態
SPA 首屏載入優化白屏、LCP、bundle、API、圖片、骨架屏怎麼處理已完成
DOM 操作成本為什麼 DOM 操作貴,Style/Layout/Paint 差在哪已完成
Diff 算法Vue 如何減少真實 DOM 操作,key 和 LIS 有什麼關係已完成
渲染性能優化v-if/v-showv-for、key、虛擬列表、v-memo、事件代理已完成
響應式性能優化reactive 深度追蹤、shallowRefmarkRaw、computed/watch 成本已完成
打包與資源優化Tree Shaking、分包、第三方庫、CDN、gzip/brotli、source map已完成
請求、快取與體驗優化API 並行、快取、預取、PWA、骨架屏、樂觀更新已完成
SSR 實現原理SSR、Hydration、狀態注水、Mismatch 與架構取捨已完成

這樣規劃的好處是:總覽文負責建立思考框架,細節文負責深入原理與實作,不會讓單篇文章變成很長的雜貨架。


四、 編碼階段優化

1. 減少不必要的響應式資料

Vue 的響應式系統會追蹤資料讀取與寫入。資料越大、層級越深、被讀取的地方越多,更新成本就可能越高。

常見做法:

  • 不需要響應式的大型資料不要放進深層 reactive。
  • 大型不可變資料可以考慮 shallowRef
  • 第三方實例、圖表物件、地圖物件可以考慮 markRaw
  • 避免對大型物件做 deep watch。
  • 把狀態拆小,讓更新範圍更精準。
typescript
import { markRaw, shallowRef } from 'vue'

const chartInstance = shallowRef(null)

function initChart(el: HTMLElement) {
  chartInstance.value = markRaw(createChart(el))
}

重點不是每次都用 shallowRef,而是知道「深層響應式不是免費的」。

2. 避免 v-ifv-for 混用

在同一個節點上同時寫 v-ifv-for,可讀性差,也容易造成不必要的判斷與渲染成本。

vue
<!-- 不建議 -->
<li
  v-for="user in users"
  v-if="user.active"
  :key="user.id"
>
  {{ user.name }}
</li>

更好的做法是先把資料整理好:

typescript
const activeUsers = computed(() => {
  return users.value.filter(user => user.active)
})
vue
<li
  v-for="user in activeUsers"
  :key="user.id"
>
  {{ user.name }}
</li>

延伸讀:Vue computed 的實現原理v-if 與 v-show 的區別

3. 列表更新要有穩定 key

key 不是為了消除 warning,而是讓 Vue 正確識別 VNode 身份。

vue
<UserRow
  v-for="user in users"
  :key="user.id"
  :user="user"
/>

動態列表如果用 index 當 key,插入、刪除、排序時容易造成 DOM 或元件狀態錯位,也會讓 Diff 更難做出正確判斷。

延伸讀:Vue 中 key 的作用

4. 大型列表要改渲染策略

如果一次渲染一萬筆資料,再怎麼調 key 或 Diff 都有限。

這時要改策略:

  • 分頁。
  • 虛擬列表。
  • 滾動到可視區域再載入。
  • 降低每列組件複雜度。
  • 避免每列綁定太多事件或 watcher。

這也是資深回答常見加分點:不是只在 API 層面微調,而是知道什麼時候該改渲染模型。

5. 事件與高頻操作要節流

對高頻事件要特別小心:

  • scroll
  • resize
  • mousemove
  • input
  • 拖拉排序
  • 搜尋建議

常見策略:

  • 防抖:等使用者停止輸入再送請求。
  • 節流:固定時間內最多執行一次。
  • requestAnimationFrame:把視覺更新對齊瀏覽器繪製節奏。
  • 事件代理:大量子元素點擊時,把事件放在共同父層處理。

五、 打包與載入優化

1. 分包與懶載入

首屏不需要的頁面與重型組件,不應該進入首屏主包。

typescript
const routes = [
  {
    path: '/report',
    component: () => import('@/views/ReportPage.vue'),
  },
]

大型組件也可以按需載入:

typescript
import { defineAsyncComponent } from 'vue'

const RichEditor = defineAsyncComponent(() => {
  return import('@/components/RichEditor.vue')
})

適合懶載入的東西:

  • 圖表。
  • 地圖。
  • 富文本編輯器。
  • PDF 預覽。
  • 低頻使用的管理頁。
  • 非首屏 Modal。

2. 第三方模組按需導入

常見問題是只用到一小段功能,卻把整個庫打進主包。

typescript
// 優先按需導入
import debounce from 'lodash-es/debounce'

UI library、icon、日期處理、圖表庫都要看是否支援按需導入與 Tree Shaking。

3. 壓縮、快取與 CDN

常見策略:

  • JS/CSS minify。
  • gzip / brotli。
  • 靜態資源上 CDN。
  • 檔名 hash 化。
  • 靜態資源長快取。
  • index.html 短快取或 no-cache。

典型策略:

text
index.html: no-cache
assets/*.js: max-age=31536000, immutable
assets/*.css: max-age=31536000, immutable

4. 注意工具時代背景

如果面試資料裡提到 happypack,要知道它比較偏 webpack 時代的多執行緒打包方案。

現在如果是 Vite 專案,優化方向會更常落在:

  • esbuild 預打包。
  • Rollup chunk 拆分。
  • manualChunks
  • 依賴按需導入。
  • source map 策略。
  • bundle analyzer。

能說出工具時代差異,會比單純背「happypack」更成熟。


六、 體驗與架構優化

1. 骨架屏與分階段渲染

骨架屏不能讓 API 真的變快,但可以改善白屏感。

好的骨架屏要注意:

  • 接近真實布局。
  • 避免資料回來後大幅位移。
  • 不要讓骨架屏取代真正的性能修復。

分階段渲染的思路是:

text
先顯示核心內容
-> 再載入次要模組
-> 最後載入低優先級互動或推薦內容

這通常比「所有資料都回來才顯示頁面」更好。

2. 快取要分層

快取不只一種:

  • HTTP cache。
  • CDN cache。
  • API gateway cache。
  • 前端 memory cache。
  • Pinia store cache。
  • IndexedDB / localStorage。
  • Service Worker / PWA cache。

但快取一定要處理失效問題。否則性能變好了,資料卻可能變舊。

3. SSR、SSG、預渲染不是萬靈丹

SSR 可以改善公開內容頁的首屏與 SEO,但它也帶來成本:

  • server 維運成本。
  • hydration mismatch。
  • server/client 環境差異。
  • 跨請求狀態污染。
  • 快取策略更複雜。

所以回答時可以說:

如果是公開內容頁、商品頁、SEO 頁,我會考慮 SSR 或 SSG;如果是登入後的後台系統,通常先做 CSR 的分包、快取與渲染優化,SSR 未必划算。

延伸讀:Vue SSR 的實現原理


七、 追問題庫

Q1:性能優化第一步做什麼?

先量測。

用 Network 看資源與請求,用 Performance 看 long task、Layout、Paint,用 bundle analyzer 看依賴體積。沒有定位瓶頸前,不要直接說要 SSR 或重構。

Q2:Vue 專案卡頓一定是 Diff 慢嗎?

不一定。

更常見的是:

  • 一次渲染太多 DOM。
  • key 不穩定。
  • computed/watch 做了重計算。
  • 大物件深層響應式。
  • 父組件更新牽動太多子組件。
  • 第三方套件造成 layout thrashing。

Diff 只是渲染成本的一部分。

Q3:骨架屏算不算性能優化?

算感知性能優化,但它不一定降低真實載入時間。

如果主包很大、API 很慢、圖片很重,骨架屏只能減少等待焦慮,不能取代真正的資源與請求優化。

Q4:什麼時候用虛擬列表?

當列表資料量很大,且一次渲染大量 DOM 造成卡頓時。

如果只有幾十筆資料,虛擬列表可能增加複雜度,收益不一定高。它適合幾千、幾萬筆資料,尤其是高度可預測或可以估算的列表。

Q5:SSR 一定能提升性能嗎?

不一定。

SSR 通常改善首屏內容可見與 SEO,但如果 client bundle 仍然很大、hydration 很慢、API 很慢,使用者互動可用時間仍可能不好。SSR 要搭配分包、快取、資料策略與 hydration 成本控制。


八、 實作題

題目:一個 Vue 後台頁面列表切換很卡,你怎麼排查?

可以這樣回答:

  1. 用 Performance 錄一次切換,看卡在 JS、Layout、Paint 還是資料請求。
  2. 看列表一次渲染多少 DOM,是否需要分頁或虛擬列表。
  3. 檢查 v-for 是否使用穩定 key,有沒有用 index key。
  4. 檢查每列是否有太多子組件、watcher、computed 或事件綁定。
  5. 檢查父層狀態更新是否導致整個列表重渲染。
  6. 檢查是否有深層 reactive 大資料或 deep watch。
  7. 如果有 DOM 測量或第三方表格套件,檢查是否造成強制同步佈局。
  8. 根據瓶頸選擇虛擬列表、拆分組件、狀態下沉、快取、節流或延後渲染。

這題的重點不是背「虛擬列表」四個字,而是能從資料、渲染、響應式和 DOM 成本一起排查。


九、 資深視角

1. 優化要能量化

好的性能優化要能回答:

  • 優化前指標是多少?
  • 優化後指標是多少?
  • 是哪一段時間變短?
  • 有沒有副作用?
  • 對真實使用者是否有效?

例如「主包從 1.8MB 降到 780KB」、「LCP 從 4.2 秒降到 2.1 秒」、「列表切換 long task 從 600ms 降到 120ms」,都比「感覺快很多」更有說服力。

2. 不要用複雜度換假性能

有些優化會增加維護成本:

  • 過度拆包會讓路由切換變慢。
  • 過度快取會有資料一致性問題。
  • 過度 memo 會讓程式更難理解。
  • 過早上 SSR 會提高部署與除錯成本。
  • 虛擬列表會增加高度計算、滾動同步與可及性問題。

資深回答要能說出:我知道這個方案有什麼副作用,所以我會在什麼條件下才用。

3. 性能優化要和產品路徑對齊

不是每個頁面都值得花同樣成本優化。

優先級通常是:

  • 首屏與高流量頁。
  • 付費或轉換關鍵頁。
  • 使用者每天高頻操作的頁面。
  • 影響互動體驗的卡頓點。
  • SEO 重要的公開內容頁。

工程時間有限,性能優化要服務產品目標。


總結

面向回答重點
方法論先量測、再定位、分層優化、最後驗證
首屏分包、壓縮、圖片、API、快取、骨架屏、SSR/SSG
渲染減少 DOM、穩定 key、虛擬列表、降低更新範圍
響應式避免大型深層 reactive、避免 deep watch、拆分狀態
打包Tree Shaking、按需導入、manual chunks、source map 策略
請求並行、取消、快取、預取、避免首屏阻塞
體驗骨架屏、分階段渲染、樂觀更新、避免 CLS
架構CSR、SSR、SSG、預渲染依場景取捨

一句完整的面試回答可以是:

Vue 性能優化我會先量測瓶頸,再分層處理。如果是首屏慢,會看 bundle、路由懶載入、圖片字體、API 並行、快取與 SSR/SSG;如果是互動卡,會看 long task、DOM 節點數、Diff 範圍、computed/watch 重算、響應式資料是否過大。Vue 特有的部分會注意穩定 key、避免不必要的深層 reactive、合理使用 v-if/v-show、大型列表用虛擬列表、重型組件按需載入。最後會用同樣的性能指標驗證效果,並評估拆包、快取、SSR 等方案帶來的複雜度。

延伸閱讀