本文 AI 產出,尚未審核

Vue3 元件基礎 – keep-alive 元件緩存

簡介

在單頁應用(SPA)中,切換路由或在不同的視圖之間切換時,Vue 會自動銷毀離開的元件,再根據需要重新建立它。對於 表單、列表或需要保留狀態的頁面,每次重新渲染都會導致使用者輸入的資料、滾動位置或暫存的 API 結果遺失,體驗上會顯得卡卡的。

<keep-alive> 正是為了解決這類問題而設計的:它會 緩存已經建立過的元件實例,在再次切換回來時直接復用,而不是重新執行 createdmounted 等生命週期。透過合理的緩存策略,我們可以大幅提升應用的效能與使用者體驗,同時減少不必要的 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"

Tipincludeexclude 支援正則表達式,讓你可以用模式一次過管理多個元件。

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> 包住,切換時 HomeProfile 只會 建立一次,之後的切換只會觸發 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 內的所有路由元件都會受到緩存控制。
  • includeexclude 的結合讓你 精細化管理,避免把不需要緩存的管理介面(如 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 直接控制緩存列表

建議的開發流程

  1. 先確認需求:哪些頁面需要保留狀態?哪些不需要?
  2. 在路由或元件層級加入 meta.keepAlive,讓策略集中管理。
  3. 在元件內部使用 activated / deactivated 進行狀態保存與恢復。
  4. 測試記憶體佔用:使用 Chrome DevTools 的 Performance → Memory 觀察是否有過度快取。
  5. 設定 max:若快取的元件數量較多,合理設定上限,避免 LRU 淘汰重要頁面。

實際應用場景

  1. 電商商品列表 → 商品詳情

    • 使用者在商品列表捲動至中段後點開商品詳情,返回列表時仍保留原本的捲動位置與篩選條件。
  2. 多步驟表單(如申請流程)

    • 每一步都是獨立的元件,使用 <keep-alive> 讓使用者在「上一步」與「下一步」之間切換時,已填寫的資料不會遺失。
  3. 管理後台的設定面板

    • 多個設定分頁(使用 Tab)彼此切換,保持每個分頁的表單狀態與驗證錯誤訊息。
  4. 即時搜尋結果

    • 使用者在搜尋結果頁面切換到其他頁面再回來,搜尋條件與結果快取,避免再次發送相同的 API 請求。
  5. 大型資料視覺化儀表板

    • 各個圖表元件可能需要大量計算,緩存後切換不同儀表板時可直接復用,提高渲染效能。

總結

  • <keep-alive> 是 Vue3 中 提升 SPA 效能與使用者體驗 的利器,透過快取元件實例,省去重複的生命週期與渲染成本。
  • 正確使用 includeexcludemax,以及在 activated / deactivated手動保存與恢復狀態,是避免常見陷阱的關鍵。
  • 建議以 路由 meta全局配置 方式統一管理緩存策略,讓專案維護更簡潔。
  • 在實務開發中,僅針對需要保留狀態的元件使用 keep-alive,配合適當的記憶體監控,即可達成 效能與資源的最佳平衡

掌握了上述概念與實作技巧後,你就能在 Vue3 專案中靈活運用 keep-alive,為使用者提供更流暢、即時的互動體驗。祝開發順利!