Vue 3 KeepAlive 指南:路由快取、狀態保留與記憶體控制
<KeepAlive> 是 Vue 內建的元件快取能力,用來快取暫時離開的元件實例。使用者從一個頁面離開後再返回時,篩選條件、表單草稿、捲動位置和局部 UI 狀態可以保留下來。
KeepAlive 也很容易被誤用。如果所有路由都快取,記憶體會成長;如果快取資料從不刷新,使用者會看到舊資訊;如果元件名和路由 key 沒設計好,快取行為可能和預期不同。
參考文件:
什麼時候值得使用 KeepAlive
適合使用 KeepAlive 的頁面,通常是「保留局部狀態能明顯改善體驗」的頁面:
- 帶篩選、搜尋、分頁標籤和捲動位置的列表頁。
- 多步驟表單,使用者可能中途查看其他頁面再回來。
- 本地渲染狀態較重的儀表板。
- 後台系統裡的多分頁頁面,切換時不應重置。
這些頁面不適合快取:
- 重新建立成本很低的頁面。
- 每次進入都必須顯示最新資料的頁面。
- 離開後應清空敏感資訊的頁面。
- 持有計時器、訂閱或大物件,且難以暫停的元件。
預設策略應該是「選擇性快取」,而不是快取所有路由。
基礎路由快取寫法
Vue Router 可以透過 RouterView 的 slot 暴露目前路由元件,這是包裹 KeepAlive 的常見位置。
<!-- App.vue -->
<template>
<RouterView v-slot="{ Component, route }">
<KeepAlive :include="cachedComponentNames" :max="8">
<component
v-if="route.meta.keepAlive"
:is="Component"
:key="route.meta.cacheKey || route.name"
/>
</KeepAlive>
<component
v-if="!route.meta.keepAlive"
:is="Component"
:key="route.fullPath"
/>
</RouterView>
</template>
<script setup lang="ts">
const cachedComponentNames = ["UserList", "OrderList", "ReportDashboard"]
</script>
把快取決策寫到路由 meta 中,更容易維護。
// router.ts
import type { RouteRecordRaw } from "vue-router"
export const routes: RouteRecordRaw[] = [
{
path: "/users",
name: "Users",
component: () => import("./views/UserList.vue"),
meta: {
keepAlive: true,
cacheKey: "users-list"
}
},
{
path: "/users/:id",
name: "UserDetail",
component: () => import("./views/UserDetail.vue"),
meta: {
keepAlive: false
}
}
]
如果使用 include 或 exclude,元件名要穩定,因為 Vue 會按元件名匹配快取。
<!-- UserList.vue -->
<script setup lang="ts">
defineOptions({
name: "UserList"
})
</script>
刷新資料但保留 UI 狀態
最常見的問題是快取資料過期。使用者返回快取頁面時,列表資料可能已經不是最新。
可以在 onActivated 中判斷是否需要刷新。不要每次啟用都無腦重載,否則快取帶來的狀態保留價值會下降。
<script setup lang="ts">
import { onActivated, onDeactivated, ref } from "vue"
const users = ref<User[]>([])
const loading = ref(false)
const lastLoadedAt = ref(0)
const STALE_AFTER_MS = 60_000
async function loadUsers() {
loading.value = true
try {
users.value = await fetchUsers()
lastLoadedAt.value = Date.now()
} finally {
loading.value = false
}
}
onActivated(() => {
const isStale = Date.now() - lastLoadedAt.value > STALE_AFTER_MS
if (!users.value.length || isStale) {
void loadUsers()
}
})
onDeactivated(() => {
// 暫停頁面隱藏後不應繼續執行的任務。
})
</script>
這樣既能保留篩選、捲動等局部狀態,也能在資料過期時刷新。
保留捲動位置
KeepAlive 會保留元件實例,但捲動位置仍可能受版面、過渡動畫或容器捲動影響。
如果頁面使用內部捲動容器,可以手動保存和恢復:
<template>
<main ref="scrollEl" class="page-scroll">
<!-- list content -->
</main>
</template>
<script setup lang="ts">
import { nextTick, onActivated, onDeactivated, ref } from "vue"
const scrollEl = ref<HTMLElement | null>(null)
let savedTop = 0
onDeactivated(() => {
savedTop = scrollEl.value?.scrollTop ?? 0
})
onActivated(async () => {
await nextTick()
if (scrollEl.value) {
scrollEl.value.scrollTop = savedTop
}
})
</script>
如果依賴瀏覽器捲動恢復,要同時測試直接訪問、返回按鈕和程式跳轉。
控制快取數量
max 屬性可以限制 KeepAlive 保留的元件實例數量。超過限制後,Vue 會裁剪較舊的快取實例。
<KeepAlive :max="8">
<component :is="Component" />
</KeepAlive>
快取數量應按真實使用路徑設定。小型後台可能只需要幾個列表快取;多分頁系統可能需要更多。不要把 max 設得很大來掩蓋狀態管理問題。
避免記憶體洩漏
被快取的元件是停用,不是卸載。因此有些清理要放在 onDeactivated,最終清理仍要放在 onUnmounted。
需要暫停或移除:
- 計時器。
- 頁面隱藏後不該繼續執行的 WebSocket 或 SSE 監聽。
- ResizeObserver 和 IntersectionObserver。
- 全域事件監聽。
- 可以重新生成的大陣列、Map、Blob 等暫存物件。
import { onActivated, onDeactivated, onUnmounted } from "vue"
let timer: number | undefined
function startPolling() {
timer = window.setInterval(refreshStatus, 15_000)
}
function stopPolling() {
if (timer) {
window.clearInterval(timer)
timer = undefined
}
}
onActivated(startPolling)
onDeactivated(stopPolling)
onUnmounted(stopPolling)
如果反覆切換頁面後記憶體仍然成長,應使用瀏覽器開發者工具做快照分析,不要靠猜。
快取 Key 與動態路由
動態路由要認真設計 key。如果多個詳情頁使用同一個元件名和同一個 key,可能會共用同一個快取實例。
常見寫法:
<!-- 使用者列表共用一個快取實例 -->
<component :is="Component" :key="route.name" />
<!-- 每個參數對應獨立快取 -->
<component :is="Component" :key="route.fullPath" />
<!-- 使用自訂 key 控制 -->
<component :is="Component" :key="route.meta.cacheKey || route.fullPath" />
列表頁不要隨便使用 fullPath。如果查詢參數組合很多,可能建立過多快取項。列表頁通常更適合快取一個實例,把篩選條件保存在元件或 store 中。
實用規則
KeepAlive 應用於保留有價值的局部狀態,不應該被當成通用效能開關。
適合快取:
- 搜尋結果列表。
- 表單草稿。
- 圖表初始化成本較高的儀表板。
- 使用者期望快速切換的分頁。
不適合快取:
- 登入、認證頁面。
- 付款或安全敏感頁面。
- 每次進入都必須重新取得資料的頁面。
- 大量不同 ID 的詳情頁,除非有明確上限。
發布前檢查
上線路由快取前確認:
- 只有選定路由使用 KeepAlive。
- 快取元件有穩定名稱。
- 動態路由 key 是有意設計的。
max設定合理。- 需要時透過
onActivated刷新資料。 - 計時器、觀察器和監聽器會在停用時暫停。
- 敏感頁面沒有被快取。
- 返回按鈕、直接訪問和分頁切換都測試過。
KeepAlive 的價值在於狀態保留,不是自動效能魔法。
本站提供瀏覽器本地工具,免註冊即可試用 →