Vue 3 KeepAlive 指南:路由快取、狀態保留與記憶體控制

前端工程(更新於 2026年7月15日)

<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
    }
  }
]

如果使用 includeexclude,元件名要穩定,因為 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 的價值在於狀態保留,不是自動效能魔法。

本站提供瀏覽器本地工具,免註冊即可試用 →

#Vue3 KeepAlive#组件缓存#路由缓存#性能优化#include exclude#responsive component caching#layout template cache#2026#前端工程