Vue keep-alive 原理全解析(Vue2+Vue3适配)
- Vue.js
- 13天前
- 12热度
- 0评论
Vue Keep-alive 深度解析与实战示例
一、Keep-alive 原理概述
Vue 的 keep-alive 组件用于缓存组件实例,以提高应用的性能和用户体验。在页面切换时,被 keep-alive 包裹的组件会保持其状态而不被销毁,从而避免重新渲染带来的开销。
缓存机制
- 缓存的是组件实例,而非 DOM 元素。
- 组件内的数据(如表单输入、滚动位置等)会被保留,提供更好的用户体验。
- 利用 activated 和 deactivated 钩子管理组件的激活和隐藏状态。
生命周期钩子
- created:初始化阶段
- mounted:DOM 挂载后
- destroyed:销毁前
- activated:再次进入时触发,可以用于刷新或重置数据。
- deactivated:组件被缓存隐藏时触发,可用于清理资源。
二、Keep-alive 核心属性
1. include
- 作用:仅缓存名称匹配的组件(以逗号分隔的字符串或数组)。
- 示例:
<keep-alive include="Home,About"> <router-view /> </keep-alive>
2. exclude
- 作用:排除指定名称的组件(优先级高于 include,如果同时存在则以 exclude 为准)。
- 示例:
<keep-alive exclude="Login"> <router-view /> </keep-alive>
3. max
- 作用:限制缓存的最大实例数量,并使用 LRU 策略淘汰最久未使用的组件。
- 示例:
<keep-alive :max="3"> <router-view /> </keep-alive>
三、Keep-alive 缓存逻辑细节
1. 匹配规则
- 组件名称需与属性值一致(字符串或正则匹配)。
- 需显式设置组件的 name 属性,否则无法被缓存。
2. 状态保留机制
- 表单输入:缓存后再次进入时,表单数据不会丢失。
- 滚动位置:页面滚动条会停留在上一次离开的位置。
- data 数据:组件内的 data 数据保持上次的状态,避免重复初始化。
3. 动态组件与 Keep-alive 结合
<keep-alive include="ComponentA,ComponentB">
<component :is="currentComponent" />
</keep-alive>四、实战示例
示例1:路由缓存(配合 <router-view>)
适配 Vue3 组合式 API,实现指定路由组件的缓存。
<template>
<div id="app">
<router-link to="/home">首页</router-link>
<router-link to="/list">列表页</router-link>
<router-link to="/login">登录页</router-link>
<!-- 缓存指定路由组件,排除 Login 组件 -->
<keep-alive include="Home,List" exclude="Login">
<router-view />
</keep-alive>
</div>
</template>
<script setup>
import { defineOptions } from 'vue'
defineOptions({
name: 'App'
})
</script>
// List.vue
<template>
<div>
<h2>列表页</h2>
<input v-model="keyword" placeholder="搜索关键词" />
<ul>
<li v-for="item in list" :key="item.id">{{ item.name }}</li>
</ul>
</div>
</template>
<script setup>
import { ref, onActivated, onDeactivated } from 'vue'
defineOptions({
name: 'List'
})
const keyword = ref('')
const list = ref([
{ id: 1, name: 'Vue3 keep-alive 实战' },
{ id: 2, name: 'Vue3 组合式 API 用法' }
])
onActivated(() => {
console.log('列表页被激活,可执行刷新数据等操作')
})
onDeactivated(() => {
console.log('列表页被缓存,可执行清理操作')
})
</script>示例2:动态组件缓存(配合 component 标签)
<template>
<div>
<button @click="currentComponent = 'UserInfo'">用户信息</button>
<button @click="currentComponent = 'UserSetting'">用户设置</button>
<!-- 缓存两个动态组件 -->
<keep-alive include="UserInfo,UserSetting" :max="2">
<component :is="currentComponent" />
</keep-alive>
</div>
</template>
<script setup>
import { ref } from 'vue'
import UserInfo from './UserInfo.vue'
import UserSetting from './UserSetting.vue'
const currentComponent = ref('UserInfo')
</script>
// UserInfo.vue
<template>
<h2>用户信息页</h2>
<!-- 组件内容省略 -->
</template>
<script setup>
defineOptions({
name: 'UserInfo'
})
</script>
// UserSetting.vue
<template>
<h2>用户设置页</h2>
<!-- 组件内容省略 -->
</template>
<script setup>
defineOptions({
name: 'UserSetting'
})
</script>示例3:缓存组件状态重置(activated 钩子用法)
<template>
<keep-alive include="FormPage">
<component :is="currentComponent" />
</keep-alive>
</template>
<script setup>
import { ref } from 'vue'
import FormPage from './FormPage.vue'
const currentComponent = ref('FormPage')
</script>
// FormPage.vue
<template>
<form>
<input v-model="form.name" placeholder="姓名" />
<input v-model="form.age" placeholder="年龄" />
</form>
</template>
<script setup>
import { ref, onActivated } from 'vue'
defineOptions({
name: 'FormPage'
})
const form = ref({
name: '',
age: ''
})
onActivated(() => {
form.value.name = ''
form.value.age = ''
})
</script>通过这些示例,可以更好地理解和使用 Vue 的 keep-alive 组件,从而在开发中实现更好的性能优化和用户体验。同时需要注意合理配置缓存策略以避免内存占用过大。
四、Vue3 keep-alive 核心缓存策略(重点)
Vue3 中 keep-alive 并非单一缓存逻辑,而是通过内置规则+属性配置+手动干预 实现多层缓存控制,核心分为四大缓存策略,覆盖日常开发全场景。每类策略均对应底层逻辑和实战用法,避免缓存滥用和内存问题。
1. 全量默认缓存策略(基础无配置)
这是 keep-alive 最基础的缓存策略,不配置任何属性时默认生效,核心是缓存所有被包裹的组件实例 ,无筛选、无数量限制,适合仅需缓存单个组件的极简场景。
核心逻辑 :组件首次挂载后存入内部 Map 缓存容器,切换时不销毁实例、仅隐藏 DOM,再次激活直接复用,全程仅执行一次 created 和 mounted 钩子。
<!-- 全量缓存示例:缓存 router-view 内所有路由组件 -->
<keep-alive>
<router-view />
</keep-alive>对应实战场景:适合单个路由组件缓存(如仅首页缓存),可将示例1中 include="Home,List" exclude="Login" :max="2" 简化为无任何属性配置,即 <keep-alive><router-view /></keep-alive> ,需注意避免多组件场景使用。
注意:该策略不适合多组件场景,会无限制占用内存,频繁切换多组件时严禁直接使用,必须搭配范围控制属性。
2. 范围筛选缓存策略(精准控制)
通过 include(白名单) 和 exclude(黑名单) 属性实现精准筛选,是企业级开发最常用的策略,解决“只缓存需要保留状态的组件”核心需求。二者优先级:exclude > include。
(1)白名单缓存策略(include)
仅缓存组件 name 匹配的组件,未匹配组件完全不缓存,每次切换都会重新创建销毁,适合指定少数核心页面缓存。
<!-- 仅缓存 Home、List 两个路由组件 -->
<keep-alive :include="['Home','List']">
<router-view />
</keep-alive>对应实战示例:参考“示例1:Vue3 路由缓存”,其中 include="Home,List" 就是典型的白名单策略,仅缓存首页和列表页,排除登录页,贴合企业级路由缓存高频场景。
(2)黑名单缓存策略(exclude)
排除指定组件,其余被包裹组件全部缓存,适合大部分组件需要缓存、仅少数组件无需缓存的场景。
<!-- 缓存所有组件,排除 Login、Detail 组件 -->
<keep-alive exclude="Login,Detail">
<router-view />
</keep-alive>对应实战示例:可基于示例1修改,将 include="Home,List" 改为 exclude="Login",即可实现“缓存所有路由组件,仅排除登录页”,与示例1的路由缓存场景一致。
关键要求:该策略依赖组件 name,Vue3 组合式API中必须用 defineOptions 显式声明 name,自动生成的默认name易匹配失败。
3. LRU 淘汰缓存策略(内存优化)
通过 max 属性配合内置 LRU(最近最少使用) 算法实现,是 Vue3 自带的内存保护策略,专门解决多组件缓存导致的内存溢出问题。
核心逻辑 :设定最大缓存数量,当缓存实例数超过 max 值时,自动删除最久未被激活使用 的组件缓存,保留近期高频使用的组件实例,平衡性能与内存占用。
<!-- 最多缓存3个组件,超出则触发LRU淘汰 -->
<keep-alive :include="['Home','List','User','Setting']" :max="3">
<router-view />
</keep-alive>对应实战示例:参考“示例2:Vue3 动态组件缓存”,其中 :max="2" 就是LRU淘汰策略的应用,限制缓存UserInfo和UserSetting两个组件,若新增组件切换(如新增UserCenter),会自动淘汰最久未使用的组件。
4. 手动干预缓存策略(灵活控制)
属于进阶策略,突破内置属性限制,通过 ref 获取 keep-alive 实例 ,直接操作内部 Map 缓存容器,实现手动清除指定/全部缓存,适合需要动态重置缓存的场景(如退出登录、表单提交后清空缓存)。
核心逻辑 :Vue3 中 keep-alive 实例暴露 cache 属性(Map 类型),可通过遍历、删除键值对实现手动清缓存,还可配合组件 unmount 彻底销毁实例。
<template>
<button @click="clearTargetCache">清空列表页缓存</button>
<button @click="clearAllCache">清空全部缓存</button>
<keep-alive ref="keepAliveRef" include="Home,List,User">
<router-view />
</keep-alive>
</template>
<script setup>
import { ref } from 'vue';
const keepAliveRef = ref(null);
// 手动清除指定组件(List)缓存
const clearTargetCache = () => {
const cacheMap = keepAliveRef.value?.cache;
if (!cacheMap) return;
for (const [key, instance] of cacheMap.entries()) {
if (instance.type.name === 'List') {
cacheMap.delete(key);
// 彻底销毁实例
instance.unmount();
}
}
};
// 清空全部缓存
const clearAllCache = () => {
const cacheMap = keepAliveRef.value?.cache;
if (!cacheMap) return;
for (let key of cacheMap.keys()) {
let componentInstance = cacheMap.get(key);
componentInstance.unmount();
cacheMap.delete(key);
}
};
</script>| 对比维度 | Vue2 | Vue3 |
|---|---|---|
| 缓存容器 | 使用普通对象(Object)存储 | 使用 Map 存储,性能更优,支持更灵活的 key 类型 |
| 组件 name 要求 | 必须显式设置 name,否则无法匹配缓存 | 未显式设置 name 时,会自动生成默认名称(基于组件文件路径),但仍建议显式设置 |
| 生命周期钩子 | activated/deactivated 钩子在组件内直接定义 | 选项式 API 用法与 Vue2 一致;组合式 API 中需使用 onActivated、onDeactivated 钩子 |
| 底层实现 | 基于 Vue 实例的 $destroy 方法拦截 | 基于组件的 unmount 生命周期拦截,与 Composition API 适配更友好 |
| 缓存策略拓展 | 仅基础筛选+LRU,无便捷手动清缓存方式 | 支持直接操作 Map 缓存,手动清缓存更便捷 |
注意:Vue3 中,keep-alive 不支持包裹多个根节点的组件,否则会抛出警告并失效,需确保被包裹的组件只有一个根节点。
五、常见使用场景与注意事项
1. 常见使用场景
- 路由切换场景:如首页、列表页、详情页切换,缓存列表页状态(避免重新请求数据、重置滚动位置);
- 动态组件切换场景:如标签页、步骤条,缓存每个标签/步骤的组件状态;
- 表单场景:如长表单分页填写,缓存已填写的表单数据,避免切换分页时数据丢失。
2. 缓存策略专属注意事项
- 范围策略必配name:使用include/exclude时,Vue3组合式API必须用defineOptions声明name,禁止依赖自动生成name;
- LRU策略max值合理设置:max数值建议按业务高频页面数量设定,一般设3-5即可,不宜过大或过小;
- 手动清缓存需彻底:删除缓存后建议调用unmount销毁实例,避免残留实例导致内存泄漏;
- 禁止策略冲突:不同时配置冲突的include和exclude,避免缓存不生效;
- 动态路由缓存适配:动态路由组件需保证name固定,否则范围策略匹配失效;
- 缓存状态按需重置:即便用了缓存策略,仍需在onActivated钩子中处理状态重置,避免旧数据干扰。
3. 通用关键注意事项
- 避免过度缓存:不要缓存所有组件,尤其是一次性使用、无需保留状态的组件(如登录页),否则会增加内存占用,反而影响性能;
- 缓存组件的生命周期差异:被缓存的组件,created、mounted 仅执行一次,后续渲染仅触发 activated ,卸载仅触发 deactivated ;
- 避免缓存带定时器/事件监听的组件:若组件内有定时器、事件监听,需在 deactivated 钩子中清除,在 activated 钩子中重新初始化,避免内存泄漏;
- Vue3 多根组件限制:keep-alive 包裹的组件必须是单根节点,否则缓存失效并抛出警告。