Vue2里的keep alive该怎么用?这些场景和细节你得清楚
做前端开发时,不少同学在Vue2项目里遇到组件缓存需求,第一个想到的就是keep - alive,但真正用起来,要么搞不懂配置逻辑,要么碰到缓存控制、生命周期变化这些问题就懵圈,今天咱把keep - alive的用法、场景、避坑点全拆开来聊,帮你把这个知识点彻底搞透。
keep - alive到底是用来干啥的?
简单说,keep - alive是Vue内置的一个组件,核心作用是缓存组件实例,平常组件切换时,Vue会销毁旧组件、创建新组件,要是组件里有请求数据、操作DOM这些开销大的操作,频繁销毁创建就会拖慢性能,有了keep - alive,组件实例会被“存起来”,下次再渲染时直接用缓存的实例,不用重新走创建、挂载那套流程,性能自然就上去了。
举个常见场景:列表页点进详情页,再返回列表页,要是没keep - alive,列表页得重新请求数据、重新渲染;用了keep - alive,列表页的组件实例还在,数据和滚动位置都能保留,用户体验更流畅。
不过得注意,keep - alive缓存的是组件实例,不是单纯的DOM节点(虽然表现上好像DOM也没被销毁,但本质是组件实例的状态被保留了),而且它只对动态组件或者路由组件生效,普通静态组件包裹了也没用。
keep - alive最基础的用法咋实现?
想让keep - alive生效,关键是用它包裹需要缓存的组件,再结合场景选配置项,分这几种情况讲:
直接包裹路由组件
如果想缓存所有路由对应的组件,在
<template>
<div id="app">
<keep - alive>
<router - view></router - view>
</keep - alive>
</div>
</template>
这样所有通过路由渲染的组件,切换时都会被缓存,但实际项目里很少全缓存,更多是按需缓存。
用include/exclude指定缓存范围
要是只想缓存部分组件,得用include(白名单)或exclude(黑名单),值是组件的name(不是标签名)。
<keep - alive include="Home,ArticleList">
<router - view></router - view>
</keep - alive>
这里只有name为Home、ArticleList的组件会被缓存,其他组件切换时照样销毁创建,反过来,exclude是排除,写法类似:
<keep - alive exclude="Login,PayPage">
<router - view></router - view>
</keep - alive>
注意exclude优先级比include高,如果一个组件同时出现在两个列表里,会被排除,而且组件必须显式定义name选项,像这样:
export default {
name: 'ArticleList', // 必须写name,否则include/exclude识别不到
data() { ... }
}
给动态组件做缓存
项目里常用
<keep - alive>
<component :is="currentComponent"></component>
</keep - alive>
currentComponent切换时,对应的组件实例会被缓存,下次切回来不用重新初始化。
想灵活控制缓存策略,咋做?
实际项目里,缓存需求不会那么“一刀切”,经常要根据路由、业务状态动态决定要不要缓存,这时候得结合更多技巧:
按路由配置控制缓存
最常见的是给路由加meta字段,标记是否需要缓存,比如在router/index.js里:
const routes = [
{
path: '/home',
component: Home,
meta: { keepAlive: true } // 标记该路由组件需要缓存
},
{
path: '/detail',
component: Detail,
meta: { keepAlive: false } // 不需要缓存
}
]
然后在App.vue里,用v - if控制
<keep - alive>
<router - view v - if="$route.meta.keepAlive"></router - view>
</keep - alive>
<router - view v - else></router - view>
这样只有meta.keepAlive为true的路由组件才会被缓存,灵活又好维护。
用业务状态动态开关缓存
有时候缓存逻辑和路由没关系,得靠页面内的状态控制,比如用户点“草稿箱”时缓存表单,点“新建”时不缓存,这时候可以给
<keep - alive v - if="isDraftMode">
<FormComponent />
</keep - alive>
<FormComponent v - else />
isDraftMode是data里的布尔值,根据业务逻辑切换,就能动态控制缓存是否生效。
用max限制缓存数量
如果页面有很多可切换的组件(比如多标签页),全缓存会占内存,这时候用max属性限制缓存实例的数量,超出的按LRU(最近最少使用)规则淘汰:
<keep - alive max="5">
<router - view></router - view>
</keep - alive>
这样最多缓存5个组件实例,旧的、很久没访问的会被销毁,平衡性能和内存占用。
缓存后,组件的生命周期有啥变化?
组件被keep - alive缓存后,原来的created、mounted这些钩子只会执行一次(因为组件实例没被销毁,只是被隐藏了),那组件激活、停用的时候咋做逻辑?Vue给了两个专属钩子:
activated:组件被激活时触发
当组件从缓存中被“拉出来”显示时,activated钩子会执行,比如列表页被缓存后,每次返回列表页(激活时)要刷新数据,就不能把请求写在created里(只执行一次),得放到activated里:
export default {
name: 'ArticleList',
activated() {
this.fetchData() // 每次激活都请求数据(如果需要实时刷新)
},
methods: {
fetchData() { ... }
}
}
deactivated:组件被停用时触发
组件被切换出去、进入缓存时,deactivated钩子执行,比如表单组件缓存前,要把临时数据存到Vuex或LocalStorage,就可以在deactivated里处理:
export default {
name: 'Form',
deactivated() {
this.saveTempData() // 停用前保存临时数据
},
methods: {
saveTempData() { ... }
}
}
记住这俩钩子的执行时机:activated在mounted之后(如果是首次渲染,先mounted再activated;之后激活只走activated),deactivated在组件隐藏时触发,和destroyed完全不一样(destroyed是组件销毁时才执行)。
碰到keep - alive不生效、缓存异常,咋排查?
项目里最头疼的是“明明用了keep - alive,咋没效果?”这类问题,常见坑点总结这几个方向:
组件名匹配问题
include/exclude是根据组件的name判断的,所以必须给组件显式写name,而且大小写、拼写要和配置里的一致,比如组件name是UserCenter,include里写成usercenter就会不生效,得严格匹配。
路由嵌套导致的层级问题
如果是嵌套路由,要注意
<!-- 父组件Layout -->
<template>
<div>
<keep - alive> <!-- 这里包的是子路由的router - view -->
<router - view></router - view>
</keep - alive>
</div>
</template>
如果父组件的router - view没被keep - alive包裹,子路由组件切换时就不会被缓存。
动态组件切换时的缓存失效
用
强制刷新缓存的技巧
有时候需要“清空缓存,重置组件状态”(比如用户登出后),这时候可以用key属性,给
<keep - alive :key="cacheKey">
<router - view></router - view>
</keep - alive>
当cacheKey从1变成2,keep - alive的缓存就会重置,下次路由切换时组件重新初始化。
实际项目里,keep - alive有哪些典型场景?
光懂原理不够,得结合业务场景才知道咋用,这几个场景特常见:
列表页与详情页的来回切换
比如电商APP的商品列表页,点进商品详情再返回,列表页的滚动位置、筛选条件要保留,做法是:
- 给列表页路由加meta.keepAlive = true;
- 在App.vue用
包裹需要缓存的路由组件; - 列表页的请求逻辑放到activated钩子,保证每次返回都能拿到最新数据(如果需求是实时刷新)。
后台管理系统的多标签页
后台系统常用多标签页展示不同页面,每个标签页对应一个组件,用keep - alive缓存这些标签页,结合max属性控制数量(比如最多开10个标签),避免内存溢出,同时用LRU策略,最近打开的标签保留,很久没访问的自动销毁。
表单页面的草稿缓存
用户填长篇表单时,切换页面再回来,内容不能丢,把表单组件用keep - alive包裹,
- 在deactivated钩子保存临时数据到Vuex或LocalStorage;
- 在activated钩子恢复数据(如果是编辑场景,可能需要重新拉接口,但新增场景直接用缓存数据)。
keep - alive和Vuex、路由守卫咋联动?
项目里很少孤立用keep - alive,经常要和其他功能配合:
用Vuex全局控制缓存状态
比如全局开关“是否缓存所有页面”,在Vuex里存一个keepAliveGlobal状态:
// store/index.js
state: {
keepAliveGlobal: false
},
mutations: {
toggleKeepAlive(state, value) {
state.keepAliveGlobal = value
}
}
然后在App.vue里:
<keep - alive v - if="$store.state.keepAliveGlobal">
<router - view></router - view>
</keep - alive>
<router - view v - else></router - view>
这样通过Vuex的mutation就能全局控制缓存是否开启。
路由守卫里处理缓存逻辑
路由守卫(比如beforeRouteEnter、beforeRouteLeave)可以结合缓存做权限判断或数据处理,比如用户离开列表页时,在beforeRouteLeave里标记是否需要缓存:
export default {
name: 'ArticleList',
beforeRouteLeave(to, from, next) {
// 根据业务逻辑决定是否保留缓存
from.meta.keepAlive = to.name !== 'Login' // 去登录页就不缓存列表页
next()
}
}
不过要注意,路由守卫的执行时机和keep - alive的生命周期不同,得理清楚逻辑顺序,避免冲突。
keep - alive核心是缓存组件实例提升性能,但实际用的时候,得结合include/exclude、路由meta、max这些配置灵活控制,还要注意生命周期变化和常见坑点,把这些细节吃透,不管是列表页缓存、多标签页还是表单缓存,都能游刃有余地实现~要是你还有具体场景拿不准,评论区留言,咱再细聊!
版权声明
本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。
发表评论:
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。