人人都会AI编程

10.3 keep-alive 组件缓存

更新时间:2026-07-11

在 Vue 应用中,组件切换是一个非常常见的需求:Tab 页签切换、步骤条前进后退、路由跳转再返回……默认情况下,每当一个组件被切换掉(比如 v-if 变为 false 或者动态组件 <component :is> 切换),它就会被销毁,下次再切回来时,组件会被重新创建,所有内部状态都回到初始值。

这就带来一个实际问题:用户在一个表单里填了一半的信息,切换到另一个标签页看了一眼,再切回来,表单已经空了——因为组件销毁后所有数据都丢失了。

<KeepAlive> 就是用来解决这个问题的。它会缓存被包裹的非活跃组件实例,而不是直接销毁它们。当组件再次被激活时,之前的状态、DOM 结构、滚动位置都会被完整还原。

基础用法

把动态组件或 <router-view><KeepAlive> 包裹起来即可:

<!-- 动态组件缓存 -->
<KeepAlive>
  <component :is="currentTab" />
</KeepAlive>

<!-- 路由视图缓存 -->
<KeepAlive>
  <router-view />
</KeepAlive>

现在当 currentTab'TabA' 切换到 'TabB' 时,TabA 的组件实例不会被销毁,而是被缓存到内存中。切回 'TabA' 时直接复用缓存实例,所有状态保持原样。

控制哪些组件需要缓存

不是所有组件都适合缓存——比如一个实时数据大屏组件,你希望它每次激活时都重新拉取最新数据,而不是显示三分钟前的旧内容。<KeepAlive> 提供了三个配置来精细控制:

includeexclude:通过组件名称(name 选项)来白名单或黑名单缓存。

<!-- 只缓存名为 UserList 和 OrderList 的组件 -->
<KeepAlive :include="['UserList', 'OrderList']">
  <component :is="currentTab" />
</KeepAlive>

<!-- 不缓存名为 LiveMonitor 的组件 -->
<KeepAlive :exclude="['LiveMonitor']">
  <component :is="currentTab" />
</KeepAlive>

注意:组件必须显式定义 name 选项或 <script setup> 中的命名,否则 include/exclude 无法匹配。

max:限制最大缓存实例数。当缓存数量超过 max 时,最久未访问的组件实例会被销毁,释放内存。

<KeepAlive :max="5">
  <component :is="currentTab" />
</KeepAlive>

缓存组件独有的生命周期钩子

<KeepAlive> 包裹的组件会额外获得两个生命周期钩子:

  • activated:组件从缓存中被激活时调用(第一次创建并激活时也会调用)。
  • deactivated:组件被缓存停用时调用(而不是 unmounted)。
<script setup>
import { onActivated, onDeactivated } from 'vue'

onActivated(() => {
  // 从缓存激活:可以在这里重新拉取最新数据
  console.log('组件激活了,可能需要刷新数据')
})

onDeactivated(() => {
  // 组件被缓存停用:可以暂停定时器、取消网络请求等
  console.log('组件停用了,可以清理临时任务')
})
</script>

这非常实用:你可以在组件缓存时暂停一个轮询定时器,激活时再重新开启,避免后台无意义的网络请求。或者切换到列表详情页再返回时,在 activated 里判断是否需要刷新数据。

工作原理(简单理解)

<KeepAlive> 内部维护了一个缓存池。当包裹的组件被切换走时,它会将组件实例的 VNode 和对应的 DOM 元素移入缓存,而不是调用 unmount 销毁。同时保存该组件的渲染状态、响应式数据、DOM 引用等一切信息。当组件再次被激活时,直接从缓存池取出实例,重新插入到 DOM 树中,并触发 activated 钩子。

因为缓存的只是组件实例(JavaScript 对象)和内存中的 DOM 引用,并没有重新执行创建、挂载等昂贵的初始化过程,所以切换速度极快,几乎是瞬间完成

适用场景与性能收益

  • 表单保持:多步骤表单、筛选面板,切换时不丢失已填写的数据。
  • Tab 页签切换:用户在一个页签操作滚动位置或输入内容,切换到另一个页签后返回,一切如初。
  • 列表-详情页往返:从商品列表点击进入详情,返回后列表保留之前的滚动位置和分页状态,而不需要重新请求第一页数据。

性能收益主要体现在两方面:

  1. 切换开销大幅降低:无缓存时,每次切换都要销毁旧组件、创建新组件、执行完整的挂在流程;有缓存时,激活一个已缓存的组件只涉及 DOM 重新插入和钩子调用,耗时通常是微秒级别。
  2. 减少不必要的网络请求:配合 activated/deactivated 实现智能数据刷新策略(比如“仅在数据可能过期时重新请求”),避免每次切换都重新拉取相同数据。

一个容易踩的坑

<KeepAlive> 只会缓存直接子组件,如果你的动态组件或路由视图被嵌套在多层 <div> 或过渡组件里面,需要确保 <KeepAlive> 紧贴需要缓存的组件。例如:

<!-- 错误:KeepAlive 子节点是 <div>,无法缓存组件 -->
<KeepAlive>
  <div>
    <component :is="tab" />
  </div>
</KeepAlive>

<!-- 正确:KeepAlive 直接包裹动态组件 -->
<KeepAlive>
  <component :is="tab" />
</KeepAlive>

如果必须在外层加包装,可以让组件成为 <KeepAlive> 的唯一直接子级。

总的来说,<KeepAlive> 是 Vue 中一个成本极低但收益极高的性能优化手段。它用几行标签就解决了“状态保持”和“切换性能”这两个在复杂交互中频繁出现的问题,是一种用好了能显著提升用户体验的内置能力。