keep-alive
keep-alive缓存状态
keep-alive 标签为 <keep-alive></keep-alive>,是 Vue 内置的一个组件,可以使被包含的组件保留状态或避免重新渲染。在之前 7.4 节提到过复用的概念,这里有些类似,但是又不完全一样,当 keep-alive
应用在 <route-view> 上时,导航的切换会保留切换之前的状态,如示例代码7-11-1所示。
<div id="app">
<p>
<router-link to="/page">page</router-link>
<router-link to="/user">user</router-link>
</p>
<router-view v-slot="{ Component }">
<keep-alive :include="['page']">
<component :is="Component"></component>
</keep-alive>
</router-view>
</div>
// 创建 User 组件
const User = {
template: '<div><input type="range" /></div>'
}
// 创建 Page 组件
const Page = {
name: 'page',
template: '<div><input type="text" /></div>'
}
// 设置路由信息
const router = VueRouter.createRouter({
history: VueRouter.createWebHashHistory(),
routes: [
{ path: '/page', component: Page },
{ path: '/user', component: User },
]
})
const app = Vue.createApp({})
app.use(router)
app.mount("#app")
在上面的示例代码中补全 HTML 内容和 Script 内容后,可以在浏览器中运行。我们分别在 User
和 Page
组件中的 template
定义了文本输入框和滑动选择器,当输入文字或者调整滑块位置切换回来之后,这些状态都被保存了下来,如图7-4所示。

注意,在 Vue Router 4 中,<keep-alive> 必须通过 v-slot
插槽才能应用在 <router-view> 上,同时需要借助动态组件 <component>,v-slot
的第二个参数 route
则提供了当前的路由对象,可以借助其传递一些路由参数,或者是做一些逻辑判断,代码如下:
// 路由组件传参
<router-view v-slot="{ Component, route }">
<component :is="Component" v-bind="route.params"></component>
</router-view>
// 逻辑判断显示404页面
<router-view v-slot="{ Component, route }">
<component v-if="route.matched.length > 0" :is="Component"/>
<div v-else>Not Found</div>
</router-view>
<router-view> 也是一个组件,如果直接被包含在 <keep-alive> 里面,所有路径匹配到的视图组件都会被缓存,也就是说如果只对某个或者某几个路径的路由进行缓存,<keep-alive> 也支持 include/exclude
设置项,如示例代码7-13-2所示。
<router-view v-slot="{ Component }">
<keep-alive :include="['page']">
<component :is="Component"></component>
</keep-alive>
</router-view>
...
const User = {
name:'user',
template: '<div><input type="range" /></div>',
}
const Page = {
name:'page',
template: '<div><input type="text" /></div>',
}
上面的代码中,只有 page
组件的内容会被缓存。include/exclude
可以设置单个字符串或者正则表达式,也可以是一个由字符串或正则表达式组成的数组,匹配的内容是组件名称,include
表示需要缓存的组件,exclude
表示不需要缓存的组件。这里需要注意组件名称是组件的 name
属性,不是在设置路由信息时命名路由的 name
:
{ path: '/page', component: Page ,name:'page' }// 不是这个name
利用元数据meta控制keep-alive
有时,在不想通过 name
来设置缓存的组件时(例如在有些应用场合,无法提前得知组件的名称),也可以利用之前讲解的元数据 meta 来设置是否需要缓存,如示例代码7-13-3所示。
<div id="app">
<p>
<router-link to="/page">page</router-link>
<router-link to="/user">user</router-link>
</p>
<router-view v-slot="{ Component }">
<keep-alive :include="includeList">
<component :is="Component"></component>
</keep-alive>
</router-view>
</div>
...
// 创建 User 组件
const User = {
name: 'user',
template: '<div><input type="range" /></div>'
}
// 创建 Page 组件
const Page = {
name: 'page',
template: '<div><input type="text" /></div>'
}
// 设置路由信息
const router = VueRouter.createRouter({
history: VueRouter.createWebHashHistory(),
routes: [
{
path: '/page',
component: Page,
name: 'page', // 需要配置命名路由和组件名称保持一致
meta: {
keepAlive: false
}
},
{
path: '/user',
component: User,
name: 'user', // 需要配置命名路由和组件名称保持一致
meta: {
keepAlive: true
}
},
]
})
const app = Vue.createApp({
data() {
return {
includeList: []
}
},
watch: {
'$route'(to) {
// 监听路由变化,把配置路由中 keepAlive 为 true 的 name 添加到 include 动态数组中
if (to.meta.keepAlive && this.includeList.indexOf(to.name) === -1) {
this.includeList.push(to.name)
}
}
}
})
app.use(router)
app.mount("#app")
注意,还需要借助 include
来实现,只是 include
的值是依据 meta
中的 keepAlive
属性来动态添加的,同时需要配置命名路由和组件名称保持一致。
当把 <keep-alive> 应用在 <router-view> 上进行路由切换时,实际上组件是不会被销毁的,例如从 User
切换到 Page
,除了第一次之外,User
和 Page
的生命周期方法(例如 created
、mounted
等)都不会触发。但是如果没有使用 keep-alive 进行缓存,那么就相当于进行路由切换时,组件都被销毁了,当切换返回时,组件都会被重新创建,当然组件的生命周期方法都会被执行。可以使用下面的代码来进行验证。
const User = {
name:'user',
template: '<div><input type="range" /></div>',
created(){
console.log('created') // created生命周期
},
mounted(){
console.log('mounted') // mounted生命周期
}
}
const Page = {
name:'page',
template: '<div><input type="text" /></div>',
created(){
console.log('created') // created生命周期
},
mounted(){
console.log('mounted') // mounted生命周期
}
}
但是,在组件生命周期方法中,有两个特殊的方法:activated
和 deactivated
。activated
表示当 vue-router 的页面被打开时,会触发这个钩子函数;deactivated
表示当 vue-router 的页面被关闭时,会触发这个钩子函数。有了这两个方法,就可以在组件中得到页面切换的时机,如示例代码7-13-4所示。
const User = {
template: '<div><input type="range" /></div>',
activated(){
console.log('activated')
},
deactivated(){
console.log('deactivated')
}
}
const Page = {
template: '<div><input type="text" /></div>',
activated(){
console.log('activated')
},
deactivated(){
console.log('deactivated')
}
}
除了使用组件生命周期方法之外,使用组件内的守卫方法 beforeRouteEnter
和 beforeRouteLeave
也可以达到相同的效果。注意之前讲的复用问题,路由切换时需要两个不同的组件才可以使用。<keep-alive> 不仅在 vue-router 中应用得比较广泛,在一般的组件中也是可以使用的。