<keep-alive> 是 Vue3 内置的抽象组件 (自身不会渲染为真实 DOM 元素),核心作用是缓存包裹在其中的组件实例,保留组件的状态和 DOM 结构,避免组件反复创建和销毁带来的性能损耗,常用于需要保留状态的场景(如标签页切换、列表页返回详情页等)。
一、核心特性与作用
1. 核心功能
- 缓存组件状态 :被
<keep-alive>包裹的组件,在切换隐藏时不会触发unmounted(销毁),而是被缓存起来;再次显示时不会触发mounted(重新创建),而是恢复之前的状态。 - 优化性能:避免组件反复创建 / 销毁、数据重新请求、DOM 重新渲染,减少资源消耗。
- 保留组件上下文:比如表单输入内容、滚动条位置、组件内部的状态数据等,切换后仍能保持原有状态。
2. 关键特点
- 是抽象组件,不生成 DOM 节点,也不会出现在组件的父组件链中;
- 仅对动态组件 (
<component :is="componentName">)或路由组件生效; - 可通过属性配置缓存规则(指定缓存 / 排除缓存的组件)。
二、基本使用方式
1. 基础用法:包裹动态组件
用于切换多个组件时,缓存不活跃的组件状态:
js
<template>
<div>
<!-- 切换按钮 -->
<button @click="currentComponent = 'ComponentA'">组件A</button>
<button @click="currentComponent = 'ComponentB'">组件B</button>
<!-- keep-alive 包裹动态组件,缓存组件实例 -->
<keep-alive>
<component :is="currentComponent"></component>
</keep-alive>
</div>
</template>
<script setup>
import { ref } from 'vue';
import ComponentA from './ComponentA.vue';
import ComponentB from './ComponentB.vue';
// 控制当前显示的组件
const currentComponent = ref('ComponentA');
</script>
此时切换组件 A/B,组件不会被销毁,再次切换回来时会保留之前的状态(如 ComponentA 中的输入框内容)。
2. 常用场景:包裹路由组件
在路由切换时缓存页面状态(如列表页滚动位置、筛选条件),是项目中最常用的场景:
js
<!-- App.vue 或路由出口组件 -->
<template>
<router-view v-slot="{ Component }">
<!-- 缓存路由组件 -->
<keep-alive>
<component :is="Component" />
</keep-alive>
</router-view>
</template>
三、核心属性:配置缓存规则
<keep-alive> 提供 3 个核心属性,用于灵活控制缓存的组件范围:
1. include:指定需要缓存的组件
-
类型:
String | RegExp | Array -
作用:只有名称匹配的组件才会被缓存(组件名称通过
name选项定义,Vue3 单文件组件中<script>内的name或<script setup>配合defineOptions({ name: 'xxx' })定义)。 -
示例:
js<!-- 字符串(逗号分隔多个组件名) --> <keep-alive include="ComponentA,ComponentB"> <component :is="currentComponent"></component> </keep-alive> <!-- 正则表达式(需用 v-bind 绑定) --> <keep-alive :include="/^Component/"> <component :is="currentComponent"></component> </keep-alive> <!-- 数组(需用 v-bind 绑定) --> <keep-alive :include="['ComponentA', 'ComponentB']"> <component :is="currentComponent"></component> </keep-alive>
2. exclude:指定不需要缓存的组件
-
类型:
String | RegExp | Array -
作用:名称匹配的组件不会被缓存,优先级高于
include。 -
示例:
js<keep-alive exclude="ComponentC"> <component :is="currentComponent"></component> </keep-alive>
3. max:设置缓存组件的最大数量
-
类型:
Number -
作用:限制缓存的组件实例数量,当缓存实例超过
max时,会按照「LRU(最近最少使用)」策略,销毁最久未使用的组件缓存。 -
示例:
js<!-- 最多缓存 3 个组件实例 --> <keep-alive :max="3"> <component :is="currentComponent"></component> </keep-alive>
四、缓存组件的生命周期钩子
被 <keep-alive> 缓存的组件,不会触发 mounted/unmounted,而是触发专属的生命周期钩子:
1. onActivated:组件被激活时触发
- 时机:缓存的组件从隐藏状态切换为显示状态时(第一次渲染时,会在
mounted之后触发;后续激活时,仅触发onActivated)。 - 用途:恢复组件激活后的状态(如重新监听事件、刷新数据等)。
2. onDeactivated:组件被失活时触发
- 时机:缓存的组件从显示状态切换为隐藏状态时(不会触发
unmounted)。 - 用途:清理组件失活后的资源(如取消事件监听、清除定时器等)。
示例:组件内使用钩子
js
<!-- ComponentA.vue -->
<template>
<div>组件A:<input type="text" v-model="inputValue"></div>
</template>
<script setup>
import { ref, onActivated, onDeactivated, onMounted } from 'vue';
const inputValue = ref('');
// 第一次渲染时触发(后续激活不触发)
onMounted(() => {
console.log('组件A 首次挂载');
});
// 组件被激活时触发(切换显示时)
onActivated(() => {
console.log('组件A 被激活');
// 可在此恢复滚动条位置、重新请求最新数据等
});
// 组件被失活时触发(切换隐藏时)
onDeactivated(() => {
console.log('组件A 被失活');
// 可在此取消定时器、取消事件监听等
});
</script>
五、高级用法:结合路由配置缓存
在实际项目中,常需要针对特定路由进行缓存,可通过「路由元信息(meta)」配合 <keep-alive> 实现精准缓存:
1. 配置路由元信息
在 router/index.js 中,给需要缓存的路由添加 meta.keepAlive: true:
js
// router/index.js
import { createRouter, createWebHistory } from 'vue-router';
import ListPage from '../views/ListPage.vue';
import DetailPage from '../views/DetailPage.vue';
import HomePage from '../views/HomePage.vue';
const routes = [
{
path: '/',
name: 'Home',
component: HomePage,
meta: { keepAlive: false } // 不缓存
},
{
path: '/list',
name: 'List',
component: ListPage,
meta: { keepAlive: true } // 需要缓存
},
{
path: '/detail/:id',
name: 'Detail',
component: DetailPage,
meta: { keepAlive: false } // 不缓存
}
];
const router = createRouter({
history: createWebHistory(),
routes
});
export default router;
2. 根据路由元信息缓存
在路由出口处,通过 v-if 判断路由的 meta.keepAlive 属性,决定是否缓存:
js
<!-- App.vue -->
<template>
<router-view v-slot="{ Component, route }">
<!-- 缓存需要保留状态的路由组件 -->
<keep-alive>
<component
:is="Component"
v-if="route.meta.keepAlive"
/>
</keep-alive>
<!-- 不缓存的组件直接渲染 -->
<component
:is="Component"
v-if="!route.meta.keepAlive"
/>
</router-view>
</template>
六、注意事项与常见问题
1. 注意事项
<keep-alive>仅对动态组件或路由组件生效,对普通组件(直接渲染的组件)无效;- 组件名称必须正确定义:
<script setup>中需通过defineOptions({ name: 'XXX' })定义组件名,否则include/exclude无法匹配; - 缓存的组件会占用内存,若缓存过多组件,可能导致内存泄漏,建议通过
max属性限制缓存数量; - 对于需要实时刷新数据的组件,避免使用
<keep-alive>,或在onActivated钩子中手动刷新数据。
2. 常见问题
-
问题 1 :缓存后组件数据不更新?解决方案:在
onActivated钩子中重新请求数据或更新组件状态,确保激活时获取最新数据。 -
问题 2 :
include/exclude配置不生效?解决方案:检查组件名称是否正确定义,正则 / 数组形式是否通过v-bind绑定,避免直接写字面量。 -
问题 3 :路由切换后滚动条位置未保留?解决方案:在
onDeactivated中记录滚动条位置,在onActivated中恢复滚动条位置:js// ListPage.vue import { ref, onActivated, onDeactivated } from 'vue'; // 记录滚动条位置 const scrollTop = ref(0); onDeactivated(() => { // 失活时记录滚动位置 scrollTop.value = document.documentElement.scrollTop || document.body.scrollTop; }); onActivated(() => { // 激活时恢复滚动位置 document.documentElement.scrollTop = scrollTop.value; document.body.scrollTop = scrollTop.value; });
总结
<keep-alive>是 Vue3 内置抽象组件,核心作用是缓存组件实例、保留组件状态、优化性能;- 基础用法:包裹动态组件或路由组件,通过
include/exclude/max配置缓存规则; - 生命周期:缓存组件触发
onActivated(激活)和onDeactivated(失活),替代mounted/unmounted; - 高级用法:结合路由元信息
meta.keepAlive,实现特定路由的精准缓存; - 注意:合理控制缓存数量,避免内存泄漏,需要实时刷新数据的场景在
onActivated中手动更新。