本文 AI 產出,尚未審核
Vue3 元件基礎 – keep-alive 元件緩存
簡介
在單頁應用(SPA)中,切換路由或在不同的視圖之間切換時,Vue 會自動銷毀離開的元件,再根據需要重新建立它。對於 表單、列表或需要保留狀態的頁面,每次重新渲染都會導致使用者輸入的資料、滾動位置或暫存的 API 結果遺失,體驗上會顯得卡卡的。
<keep-alive> 正是為了解決這類問題而設計的:它會 緩存已經建立過的元件實例,在再次切換回來時直接復用,而不是重新執行 created、mounted 等生命週期。透過合理的緩存策略,我們可以大幅提升應用的效能與使用者體驗,同時減少不必要的 API 呼叫與渲染成本。
本篇文章將從概念說明、實作範例、常見陷阱到最佳實踐,完整帶你掌握 Vue3 中 keep-alive 的使用方式,讓你在開發中能夠靈活運用緩存,打造更順暢的介面。
核心概念
1. keep-alive 的基本原理
<keep-alive>是一個 內建抽象元件,它不會產生實際的 DOM,僅負責管理子元件的快取與銷毀。- 被
<keep-alive>包裹的元件在第一次渲染後,會被 保留在內存,之後切換走時不會觸發unmounted,而是觸發deactivated;再次切回時則觸發activated。 - 只有 具備狀態(例如表單、搜尋結果、滾動位置)的元件才適合使用 keep-alive,對於純展示或一次性渲染的元件則不需要額外緩存。
2. 何時使用 keep-alive
| 使用情境 | 為什麼需要緩存 |
|---|---|
| Tab 切換(如商品列表 ↔ 資料詳情) | 保留使用者已捲動的位置與搜尋條件 |
| 表單編輯(多步驟表單) | 防止使用者返回上一頁時資料被清空 |
| 頻繁切換的路由(如設定頁面) | 減少重複的 API 請求與渲染成本 |
| 大型列表(虛擬滾動) | 暫存已渲染的項目,避免每次都重新計算 |
3. keep-alive 的屬性
| 屬性 | 說明 | 範例 |
|---|---|---|
include |
只緩存名稱符合條件的元件(字串、正則或陣列) | include="UserDetail" |
exclude |
排除特定名稱的元件不緩存 | exclude="/^Admin/" |
max |
設定緩存的最大個數,超過時會依照 LRU(最少最近使用)原則淘汰 | max="10" |
Tip:
include、exclude支援正則表達式,讓你可以用模式一次過管理多個元件。
4. 生命週期鈎子:activated / deactivated
export default {
name: 'UserDetail',
// 元件首次建立時會執行
created() {
console.log('created')
},
// 每次被 keep-alive 從快取中喚醒時呼叫
activated() {
console.log('activated – 從快取中恢復')
// 例如重新取得資料或恢復滾動位置
},
// 每次被 keep-alive 暫停(離開)時呼叫
deactivated() {
console.log('deactivated – 暫存狀態')
// 例如保存表單暫存、取消訂閱等
},
// 元件真正被銷毀時才會觸發
unmounted() {
console.log('unmounted – 完全移除')
}
}
程式碼範例
以下示範 4 個常見的 keep-alive 使用情境,並附上完整說明與註解。
範例 1:最簡單的 Tab 切換緩存
<!-- App.vue -->
<template>
<div>
<button @click="current = 'Home'">Home</button>
<button @click="current = 'Profile'">Profile</button>
<!-- keep-alive 包裹動態 component -->
<keep-alive>
<component :is="currentComponent" />
</keep-alive>
</div>
</template>
<script setup>
import { ref, computed } from 'vue'
import Home from './components/Home.vue'
import Profile from './components/Profile.vue'
const current = ref('Home')
const currentComponent = computed(() => (current.value === 'Home' ? Home : Profile))
</script>
說明
component會根據current動態切換。- 由於被
<keep-alive>包住,切換時Home與Profile只會 建立一次,之後的切換只會觸發activated/deactivated,保留各自的狀態(例如表單內容、滾動位置)。
範例 2:使用 include / exclude 控制緩存範圍
<!-- App.vue -->
<template>
<keep-alive :include="included" :exclude="excluded" :max="5">
<router-view />
</keep-alive>
</template>
<script setup>
import { ref } from 'vue'
const included = ref(['Dashboard', 'Settings']) // 只緩存 Dashboard、Settings
const excluded = ref(/^Admin/) // 不緩存任何以 Admin 開頭的頁面
</script>
說明
router-view內的所有路由元件都會受到緩存控制。include與exclude的結合讓你 精細化管理,避免把不需要緩存的管理介面(如 Admin)納入快取,節省記憶體。
範例 3:在 activated / deactivated 中保存與恢復滾動位置
// ListView.vue
export default {
name: 'ListView',
data() {
return {
items: [], // 假設透過 API 取得的資料
scrollTop: 0 // 用來暫存滾動位置
}
},
async created() {
// 初次載入資料
this.items = await fetchItems()
},
activated() {
// 從快取恢復時,還原滾動位置
this.$nextTick(() => {
const container = this.$el.querySelector('.list')
container.scrollTop = this.scrollTop
})
},
deactivated() {
// 離開時保存當前滾動位置
const container = this.$el.querySelector('.list')
this.scrollTop = container.scrollTop
}
}
<!-- ListView.vue template -->
<template>
<div class="list" style="height:400px; overflow:auto;">
<div v-for="item in items" :key="item.id" class="item">
{{ item.title }}
</div>
</div>
</template>
說明
- 使用
deactivated把使用者目前的滾動位置寫入scrollTop,在activated時再把它恢復。 - 這樣即使使用者在列表中瀏覽很久後切換到其他頁面,再回來時 不會被強制回到頂部,提升使用體驗。
範例 4:結合 Vue Router 的 meta 設定自動決定是否緩存
// router/index.js
import { createRouter, createWebHistory } from 'vue-router'
import Home from '@/views/Home.vue'
import Detail from '@/views/Detail.vue'
import Settings from '@/views/Settings.vue'
const routes = [
{ path: '/', component: Home, meta: { keepAlive: true } },
{ path: '/detail/:id', component: Detail, meta: { keepAlive: false } },
{ path: '/settings', component: Settings, meta: { keepAlive: true } }
]
export default createRouter({
history: createWebHistory(),
routes
})
<!-- App.vue -->
<template>
<!-- 依據路由 meta 動態決定是否包在 keep-alive 裡 -->
<keep-alive v-if="$route.meta.keepAlive">
<router-view />
</keep-alive>
<router-view v-else />
</template>
說明
- 透過路由的
meta.keepAlive屬性,讓 每個路由自行決定 是否需要被快取。 - 這樣的寫法在大型專案中非常好維護,新增或修改緩存策略只要改
meta即可,避免在主程式碼中硬編碼include/exclude。
常見陷阱與最佳實踐
| 陷阱 | 可能的後果 | 解決方式 / 最佳實踐 |
|---|---|---|
過度緩存:把所有元件都包在 <keep-alive> 中 |
記憶體佔用暴增,導致瀏覽器卡頓 | 只緩存需要保留狀態的元件,使用 include / exclude 或路由 meta 控制 |
| 忘記在 deactivated 中儲存狀態 | 切回時畫面恢復不完整(表單被清空、滾動位置回到頂部) | 在 deactivated 內手動儲存必要的 UI 狀態,或利用 Pinia / Vuex 做全局管理 |
| 使用 keep-alive 時仍觸發 mounted | 可能是因為子元件本身被 <keep-alive> 包住,而外層沒有 |
確認 keep-alive 只包住最外層需要緩存的元件,不要在子層重複使用 |
| 快取的資料過時 | 使用者返回時看到舊資料,造成錯誤判斷 | 在 activated 時重新驗證或刷新資料(例如加上 if (needsRefresh) fetch()) |
| max 設定不當 | 超過上限時會自動淘汰較舊的快取,可能導致使用者預期外的重新渲染 | 根據實際需求調整 max,或使用 include/exclude 直接控制緩存列表 |
建議的開發流程
- 先確認需求:哪些頁面需要保留狀態?哪些不需要?
- 在路由或元件層級加入
meta.keepAlive,讓策略集中管理。 - 在元件內部使用
activated/deactivated進行狀態保存與恢復。 - 測試記憶體佔用:使用 Chrome DevTools 的 Performance → Memory 觀察是否有過度快取。
- 設定
max:若快取的元件數量較多,合理設定上限,避免 LRU 淘汰重要頁面。
實際應用場景
電商商品列表 → 商品詳情
- 使用者在商品列表捲動至中段後點開商品詳情,返回列表時仍保留原本的捲動位置與篩選條件。
多步驟表單(如申請流程)
- 每一步都是獨立的元件,使用
<keep-alive>讓使用者在「上一步」與「下一步」之間切換時,已填寫的資料不會遺失。
- 每一步都是獨立的元件,使用
管理後台的設定面板
- 多個設定分頁(使用 Tab)彼此切換,保持每個分頁的表單狀態與驗證錯誤訊息。
即時搜尋結果
- 使用者在搜尋結果頁面切換到其他頁面再回來,搜尋條件與結果快取,避免再次發送相同的 API 請求。
大型資料視覺化儀表板
- 各個圖表元件可能需要大量計算,緩存後切換不同儀表板時可直接復用,提高渲染效能。
總結
<keep-alive>是 Vue3 中 提升 SPA 效能與使用者體驗 的利器,透過快取元件實例,省去重複的生命週期與渲染成本。- 正確使用
include、exclude、max,以及在activated/deactivated中手動保存與恢復狀態,是避免常見陷阱的關鍵。 - 建議以 路由 meta 或 全局配置 方式統一管理緩存策略,讓專案維護更簡潔。
- 在實務開發中,僅針對需要保留狀態的元件使用 keep-alive,配合適當的記憶體監控,即可達成 效能與資源的最佳平衡。
掌握了上述概念與實作技巧後,你就能在 Vue3 專案中靈活運用 keep-alive,為使用者提供更流暢、即時的互動體驗。祝開發順利!