Vue3 – 效能與最佳化:keep-alive 緩存
簡介
在單頁應用(SPA)中,切換路由或切換大量子組件時,若每次都重新執行掛載、銷毀的生命週期,會造成不必要的效能損耗,尤其在手機或低階裝置上更為明顯。Vue 3 提供的 <keep-alive> 組件,讓開發者可以 將已經渲染過的組件實例緩存起來,在下次需要時直接復用,省去重新渲染的成本。
本篇文章將從概念、使用方式、實務範例、常見陷阱與最佳實踐,完整說明 keep-alive 在 Vue 3 中的運作原理與最佳化策略,幫助初學者到中級開發者快速上手並在實際專案中正確應用。
核心概念
1. keep-alive 是什麼?
<keep-alive> 是一個 抽象組件(abstract component),它不會產生自己的 DOM,而是把包裹的子組件「暫存」起來。被暫存的組件會保留其 data、computed、watcher、狀態(state)以及 DOM 節點,只會觸發 activated / deactivated 兩個生命週期鉤子,而不會走 created、mounted、unmounted。
2. 何時使用 keep-alive?
- 頁籤切換:如「商品列表」與「購物車」頻繁切換,保持列表的滾動位置與已載入資料。
- 表單編輯:使用者在多步驟表單間跳來跳去,避免已填寫的資料遺失。
- 大型列表或圖表:渲染成本高的組件,切換時只需要顯示/隱藏即可。
3. 基本語法
<keep-alive>
<component :is="currentView"></component>
</keep-alive>
currentView為動態切換的組件名稱或元件物件。- 只要
<keep-alive>包住的子組件符合條件,就會被緩存。
4. 控制緩存的屬性
| 屬性 | 說明 | 範例 |
|---|---|---|
include |
只緩存符合名稱或正則的組件 | <keep-alive include="User,Settings"> |
exclude |
排除不想緩存的組件 | <keep-alive :exclude="/^Admin/"> |
max |
緩存的最大實例數,超過會依 FIFO 刪除最舊的 | <keep-alive :max="5"> |
5. 生命週期鉤子
activated:組件從緩存中被取出、重新顯示時呼叫。deactivated:組件被放入緩存、隱藏時呼叫。
注意:
mounted、unmounted只會在第一次掛載與最終銷毀時觸發。
程式碼範例
範例 1:最簡單的頁籤切換
<!-- App.vue -->
<template>
<div>
<button @click="current = 'Home'">Home</button>
<button @click="current = 'Profile'">Profile</button>
<keep-alive>
<component :is="current"></component>
</keep-alive>
</div>
</template>
<script setup>
import { ref } from 'vue'
import Home from './components/Home.vue'
import Profile from './components/Profile.vue'
const current = ref('Home')
</script>
- 說明:切換
Home、Profile時,兩個組件的狀態會被保留。若不加<keep-alive>,每次切換都會重新呼叫created、mounted。
範例 2:使用 include / exclude 控制緩存
<keep-alive include="Dashboard,Settings" :exclude="/^Admin/">
<router-view></router-view>
</keep-alive>
- 說明:只會緩存路由名稱為
Dashboard或Settings的組件;所有以Admin開頭的路由則不會被緩存,適合保護敏感頁面不留下快取。
範例 3:設定最大緩存數量 max
<keep-alive :max="3">
<router-view></router-view>
</keep-alive>
- 說明:最多同時保留 3 個組件的實例,當第 4 個被加入時,最早加入的會被自動銷毀(觸發
unmounted),防止記憶體過度佔用。
範例 4:利用 activated / deactivated 執行自訂邏輯
// Detail.vue
export default {
name: 'Detail',
data() {
return { scrollTop: 0 }
},
activated() {
// 從緩存中恢復時,還原滾動位置
this.$nextTick(() => {
this.$el.scrollTop = this.scrollTop
})
},
deactivated() {
// 隱藏前先記錄滾動位置
this.scrollTop = this.$el.scrollTop
}
}
- 說明:在列表頁返回細節頁時,使用者的滾動位置不會被重置,提升使用體驗。
範例 5:結合 v-show 與 keep-alive 的最佳化
<keep-alive>
<MyHeavyComponent v-show="showHeavy"></MyHeavyComponent>
</keep-alive>
<button @click="showHeavy = !showHeavy">Toggle Heavy</button>
- 說明:
v-show只改變display,不會觸發掛載/銷毀;配合keep-alive,在切換顯示狀態時完全不會重新渲染,適合「一次渲染、頻繁切換」的情境。
常見陷阱與最佳實踐
| 陷阱 | 可能的影響 | 解決方案 |
|---|---|---|
| 緩存過多導致記憶體飽和 | 大型表格或圖表持續累積,瀏覽器記憶體使用量急升 | 使用 max 限制緩存數量,或在不需要時手動 unmount(v-if) |
| 狀態未重置 | 表單切換後仍保留舊資料,造成錯誤提交 | 在 deactivated 中手動清除或在 activated 中重新初始化 |
與 Vue Router 搭配時忘記 key |
同一路由但不同參數的組件仍被視為同一緩存實例 | 為 <router-view> 加上 :key="$route.fullPath",確保每個參數組合都有獨立緩存 |
使用 keep-alive 包住非組件 |
會產生警告或無效緩存 | 確保 <keep-alive> 只包住 具名或匿名組件,不要直接包住普通 HTML 標籤 |
誤用 include / exclude 正則 |
正則寫錯導致所有組件皆被排除或全部緩存 | 測試正則表達式,或改用陣列形式 include=['Home','Profile'] 以避免語法錯誤 |
最佳實踐
- 先評估成本:只有在「渲染成本高」或「切換頻繁」的情況下才使用
keep-alive。 - 搭配
max:避免無限制緩存,特別是移動端。 - 使用
activated/deactivated處理需要在顯示/隱藏時執行的副作用(如 API 請求、滾動位置、計時器)。 - 在路由層面加
key:保證不同參數的路由能得到獨立的快取。 - 定期檢測記憶體:使用 Chrome DevTools 的 Performance / Memory 面板觀察快取是否過大。
實際應用場景
1. 電商商品列表與商品詳情
使用 keep-alive 緩存商品列表頁,使用者在商品詳情與列表間切換時,列表的 滾動位置、已載入的分頁資料 皆不會重新請求,減少 API 呼叫與頁面閃爍。
2. 多步驟表單(Wizard)
在每一步都使用 keep-alive,使用者可隨意前後切換,已填寫的欄位會自動保留,且不必在每次切換時重新驗證或重新取得遠端資料。
3. 管理後台的圖表儀表板
大型圖表(如 ECharts、Highcharts)渲染成本高,將圖表組件放入 <keep-alive>,切換不同儀表板時,只需顯示/隱藏,避免重繪造成卡頓。
4. 手機端的 SPA
手機裝置記憶體較受限,使用 max 限制緩存數量,同時在不活躍的頁面使用 v-if 釋放資源,兼顧效能與使用者體驗。
總結
<keep-alive> 是 Vue 3 中用來 提升切換效能、減少不必要重新渲染 的強大工具。透過 include、exclude、max 等屬性,我們可以精細控制哪些組件需要被緩存、緩存多少實例;而 activated / deactivated 生命週期則提供在「取出」與「放入」快取時執行自訂邏輯的能力。
在實際開發中,先評估渲染成本、設定合理的緩存上限,並善用生命週期鉤子清理或重置狀態,就能在不犧牲記憶體的前提下,為使用者帶來更流暢的操作體驗。希望本篇文章能讓你快速掌握 keep-alive 的核心概念與最佳實踐,並在專案中靈活運用,打造高效能的 Vue 3 應用。祝開發順利!