在平常开发中,有部分组件没有必要多次初始化,这时,我们需要将组件进行持久化,使组件的状态维持不变,在下一次展示时,也不会进行重新初始化组件。
也就是说,keepalive 是 Vue 内置的一个组件,可以使被包含的组件保留状态,或避免重新渲染 。也就是所谓的组件缓存
<keep-alive>
<component /> //你的组件
</keep-alive>
被keepalive包含的组件不会被再次初始化,也就意味着不会重走生命周期函数
但是有时候是希望我们缓存的组件可以能够再次进行渲染,这时Vue为我们解决了这个问题
被包含在 keep-alive 中创建的组件,会多出两个生命周期的钩子: activated 与 deactivated
activated 当 keepalive 包含的组件再次渲染的时候触发
deactivated 当 keepalive 包含的组件销毁的时候触发
keepalive是一个抽象的组件,缓存的组件不会被mounted,为此提供activated和deactivated钩子函数
在2.1.0 版本后keep-alive新加入了两个属性: include(包含的组件缓存生效) 与 exclude(排除的组件不缓存,优先级大于include) 。
参数理解
keepalive可以接收3个属性做为参数进行匹配对应的组件进行缓存:
include包含的组件(可以为字符串,数组,以及正则表达式,只有匹配的组件会被缓存)
exclude排除的组件(以为字符串,数组,以及正则表达式,任何匹配的组件都不会被缓存)
max缓存组件的最大值(类型为字符或者数字,可以控制缓存组件的个数)
注:当使用正则表达式或者数组时,一定要使用v-bind
代码示例:
<!-- 逗号分隔字符串 -->
<keep-alive include="a,b">
<component :is="view"></component>
</keep-alive>
<!-- 正则表达式 (使用 `v-bind`) -->
<keep-alive :include="/a|b/">
<component :is="view"></component>
</keep-alive>
<!-- 数组 (使用 `v-bind`) -->
<keep-alive :include="['a', 'b']">
<component :is="view"></component>
</keep-alive>
// 只缓存组件name为a或者b的组件
<keep-alive include="a,b">
<component />
</keep-alive>
// 组件name为c的组件不缓存(可以保留它的状态或避免重新渲染)
<keep-alive exclude="c">
<component />
</keep-alive>
// 如果同时使用include,exclude,那么exclude优先于include, 下面的例子只缓存a组件
<keep-alive include="a,b" exclude="b">
<component />
</keep-alive>
// 如果缓存的组件超过了max设定的值5,那么将删除第一个缓存的组件
<keep-alive exclude="c" max="5">
<component />
</keep-alive>
!!<keep-alive> 不会在函数式组件中正常工作,因为它们没有缓存实例。
配合router使用
router-view也是一个组件,如果直接被包在keepalive里面,那么所有路径匹配到的视图组件都会被缓存,如下:
<keep-alive>
<router-view>
<!-- 所有路径匹配到的视图组件都会被缓存! -->
</router-view>
</keep-alive>
如果只想要router-view里面的某个组件被缓存,怎么办?
//只有路径匹配到的 name 为 a 组件会被缓存
<keep-alive include="a">
<router-view></router-view>
</keep-alive>
2.使用 meta 属性
// routes 配置
export default [
{
path: '/',
name: 'home',
component: Home,
meta: {
keepAlive: true // 需要被缓存
}
}, {
path: '/profile',
name: 'profile',
component: Profile,
meta: {
keepAlive: false // 不需要被缓存
}
}
]
<keep-alive>
<router-view v-if="$route.meta.keepAlive">
<!-- 这里是会被缓存的视图组件,比如 Home! -->
</router-view>
</keep-alive>
<router-view v-if="!$route.meta.keepAlive">
<!-- 这里是不会被缓存的视图组件,比如 Profile! -->
</router-view>
!!这里需要注意的是,如果你是通过这种设置meta的方式创建keepAlive的话,keepAlive组件一旦被创建,就不会被销毁了,如果你想通过改变meta的方式销毁keepAlive,这样做是不可行的!!!网上很多教程叫你把meta的keepAlive设置为false,他们到底有没有实验过啊,真坑!那么如果你设置了keepAlive之后,又有某种特殊情况想销毁的话,那要怎么做呢?这里建议通过vuex配合设置include来决定页面是否需要keepAlive,下面是具体实现例子。
比如,你有这个需求:
首页是A页面
B页面跳转到A,A页面需要缓存
C页面跳转到A,A页面不需要被缓存
在你需要的设置缓存的页面:
<keep-alive :include="keepAlivePage">
<router-view></router-view>
</keep-alive>
<script>
computed: {
...mapGetters([
'keepAlivePage'
])
}
</script>
在vuex中(我这里把vuex按照模块划分了,如果不是用Moudules这种的话自行转义哈)
getters中
const getters = {
keepAlivePage: state => state.settings.keepAlivePage // 获取需要缓存的页面
}
export default getters
state中
const state = {
keepAlivePage: [] // 需要缓存的页面,如果说你一开始就要缓存,那么你可以在这里设置初始值,如果你不需一开始就设置缓存,那么设置为空,再通过某种条件通过mutations或者actions改变keepAlivePage
}
如果说我要增加一个页面缓存,那么我在我需要增加的代码逻辑上增加下面一行代码
doSomeThing() {
doSomeThing(1001)
.then(res => {
if (res.code === 200 && res.data.length) {
doSomeThing....
// 把页面keepAlive缓存
this.$store.dispatch('settings/addKeepAlivePage', 'Home') //'Home'就是你要增加页面缓存的名称。
}
})
然后再vuex的actions中
const actions = {
addKeepAlivePage ({ commit }, name) {
commit('ADD_KEEP_ALVE', name)
}
}
vuex的mutations中
const mutations = {
ADD_KEEP_ALVE: (state, name) => {
state.keepAlivePage = state.keepAlivePage.concat(name)
}
}
这样子‘Home’组件就会被添加到缓存之中
注意!!这里要特别注意页面组价的名字要和router设置页面的名字要一一对应,不然的话接下来的需求就会实现不了!!
接下来回到刚刚那个需求,
B页面跳转到A,A页面需要缓存
C页面跳转到A,A页面不需要被缓存
思路是在每个路由的beforeRouteLeave(to, from, next)钩子中设置修改vuex中的keepAlivePage数组,把router的Name名字和keepAlivePage数组对比,如果你要缓存直接添加上就好了,如果不需要就对比里面是否有这个Name,有的话就删除就好了
例如:我在404页面跳转到home界面的话需要清除keepAlive,那么我就可以在beforeRouteLeave
这样设置
methods: {
goTo () {
this.$router.go(-1)
}
},
beforeRouteLeave (to, from, next) {
console.log(to)
this.$store.dispatch('settings/changeKeepAlive', to.name)
next()
}
}
vuex的actions中
changeKeepAlive ({ commit }, name) {
commit('CHANGE_KEEP_ALIVE', name)
},
vuex的mutations中
CHANGE_KEEP_ALIVE: (state, name) => {
const keepAlivePage = state.keepAlivePage
const index = keepAlivePage.indexOf(name)
if (index > -1) {
keepAlivePage.splice(index, 1)
}
}
这样子我就把keepAlivePage
数组中的‘Home’删掉了,如果你要新增keepAlie页面的话,就用刚刚这个方法就好了
this.$store.dispatch('settings/addKeepAlivePage', 'Home')
OK,现在需求完美解决,有问题可以留言提问
防坑指南
1.keep-alive 先匹配被包含组件的 name 字段,如果 name 不可用,则匹配当前组件 components 配置中的注册名称。
2.keep-alive 不会在函数式组件中正常工作,因为它们没有缓存实例。
3.当匹配条件同时在 include 与 exclude 存在时,以 exclude 优先级最高(当前vue 2.4.2 version)。比如:包含于排除同时匹配到了组件A,那组件A不会被缓存。
4.包含在 keep-alive 中,但符合 exclude ,不会调用activated和 deactivated。