1. keep-alive是什么?
keep-alive是一个抽象组件,他本身不会渲染成一个dom元素,也不会出现在父组件的链式调用中。
keep-alive可以在切换组件时,保存他所包裹的组件的状态,使其不被销毁。
使用场景:
通常在项目中使用标签页切换路由组件这种情况下,我们从列表页点击【修改】某个数据项时,通过系统内打开标签页的方式打开编辑页,进行一些操作后,我们又切回了列表页,我们希望可以保留编辑页内刚刚的操作,这时使用keep-alive就可以解决这种场景。
keep-alive除了可以保存组件状态,还可以避免组件被反复创建和渲染,可以有效提升系统性能。
2. keep-alive的用法
keep-alive对外提供了三个属性,include/exclude/max 。两个生命周期,activated/deactivated。
include:对哪些进行缓存
exclude:对哪些不进行缓存
max:最多缓存多少个
3. 源码解析
export default{
name:'keep-alive',
abstract:true, //判断当前组件虚拟dom是否渲染成真实dom的关键
props:{
include:patternTypes, //缓存白名单
exclude:patternTypes, //缓存黑名单
max:[string,number], //混存的组件实例数量上限
},
created(){
this.cache = Object.create(null) //缓存虚拟dom
this.keys = [] //缓存的虚拟dom的键集合
},
destroyed(){
for(const key in this.cache){ //删除所有缓存
pruneCachEntry(this.cache,key,this.keys)
}
},
mounted(){
// <!-- 实时监听黑白名单的变动 -->
this.$watch('include',val => {
pruneCache(this,name => matches(val,name))
})
this.$watch('exclude',val => {
pruneCache(this,name => matches(val,name))
})
},
render(){
const slot = this.$slots.default
const vnode:VNode = getFirstComponentChild(slot); //找到第一个子组件对象
const componentOptions:?VNodeComponentOptions = vnode && vnode.componentOptions
if(componentOptions){ //存在组件参数
<!-- check pattern -->
const name:?string = getComponent(componentOptions) //组件名称
const {include,exclude} = this;
<!-- 条件匹配 -->
if((include && (!name || !matches(include,name))) || (exclude && name && matches(exclude,name))){
return vnode
}
const {cache ,keys} = this;
<!-- 定义组件的缓存key -->
const key:?string = vnode.key == null ? componentOptions.Ctor.cid + (componentOptions.tag ? `::${componentOptions.tag}`) : vnode.key
<!-- 已经缓存过的组件 -->
if(cache[key]){
vnode.componentInstance = cache[key].componentInstance
remove(keys,key)
keys.push(key) //调整key排序
}else{
cache[key] = vnode //缓存组件对象
keys.push(key)
if(this.max && keys.length > parseInt(this.max)){ //超过缓存数限制,将第一个删除
pruneCacheEntry(cache,keys[0],keys,this._vnode)
}
}
}
vnode.data.keepAlive = true //渲染和执行被包裹组件的钩子函数需要用到
}
return vnode || (slot && slot[0])
}