vue中keep-alive的原理

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])
}