keep-alive的源码不到两百行,比较简单,这里做一个简单的源码分析
vue/src/component/keep-alive.js
keep-alive允许传入include,exclude,以及max缓存的最大数量
props: {
include: patternTypes,
exclude: patternTypes,
max: [String, Number]
},
在初始化时,定义一个cache对象和一个keys数组
created () {
this.cache = Object.create(null)//保存缓存组件的vnode
this.keys = []//缓存组件的key值
},
渲染时,获取默认插槽,得到插槽的vnode以及组件配置
render () {
const slot = this.$slots.default
const vnode: VNode = getFirstComponentChild(slot)
const componentOptions: ?VNodeComponentOptions = vnode && vnode.componentOptions
if (componentOptions) {
// 获取组件名称,通过matchs方法来判断当前组件是否应该被缓存,如果没有组件在include中或者有组件在exclude中,就直接返回,不做任何操作
const name: ?string = getComponentName(componentOptions)
const { include, exclude } = this
if (
// not included
(include && (!name || !matches(include, name))) ||
// excluded
(exclude && name && matches(exclude, name))
) {
return vnode
}
const { cache, keys } = this
//产生一个key,如果vnode中有key值就用这个key,没有生成一个唯一的key
const key: ?string = vnode.key == null
? componentOptions.Ctor.cid + (componentOptions.tag ? `::${componentOptions.tag}` : '')
: vnode.key
//如果cache对象中有保存这个vnode实例,那么不用产生新的实例,使用缓存中原本的componentInstance身上的实例,并且将当前key值放在数组的最后一位,这样可以让它被删除的优先级降低
if (cache[key]) {
vnode.componentInstance = cache[key].componentInstance
remove(keys, key)
keys.push(key)
//如果没有这个缓存,那么将vnode赋值给vnodeToCache,key赋值给ketToCache
} else {
// delay setting the cache until update
this.vnodeToCache = vnode
this.keyToCache = key
}
vnode.data.keepAlive = true
}
return vnode || (slot && slot[0])
}
在keep-alive组件中第一个method方法,cacheVNode
这个函数的功能就是判断vnodeToCache是否存在,存在就将对应的vnode保存到cache中,key保存到keys中
cacheVNode() {
const { cache, keys, vnodeToCache, keyToCache } = this
if (vnodeToCache) {
const { tag, componentInstance, componentOptions } = vnodeToCache
cache[keyToCache] = {
name: getComponentName(componentOptions),
tag,
componentInstance,
}
keys.push(keyToCache)
// prune oldest entry
if (this.max && keys.length > parseInt(this.max)) {
pruneCacheEntry(cache, keys[0], keys, this._vnode)
}
this.vnodeToCache = null
}
}
上面的createVNode的执行时机在mounted和updated的,就是说只有挂载时或者更新时才会有机会更新缓存的vnode
mounted () {
this.cacheVNode()
this.$watch('include', val => {
pruneCache(this, name => matches(val, name))
})
this.$watch('exclude', val => {
pruneCache(this, name => !matches(val, name))
})
},
updated () {
this.cacheVNode()
},