keep-alive的使用和源码介绍
<keep-alive> 包裹动态组件时,会缓存不活动的组件实例,而不是销毁它们。和 <transition> 相似,<keep-alive> 是一个抽象组件:它自身不会渲染一个 DOM 元素,也不会出现在组件的父组件链中。
当组件在 <keep-alive> 内被切换,它的 activated 和 deactivated 这两个生命周期钩子函数将会被对应执行。
<!-- 基本使用 -->
<keep-alive>
<component :is="view"></component>
</keep-alive>
<!-- 多个条件判断的子组件 -->
<keep-alive>
<comp-a v-if="a > 1"></comp-a>
<comp-b v-else></comp-b>
</keep-alive>
include 和 exclude prop 允许组件有条件地缓存。二者都可以用逗号分隔字符串、正则表达式或一个数组来表示
max:最多可以缓存多少组件实例。一旦这个数字达到了,在新实例被创建之前,已缓存组件中最久没有被访问的实例会被销毁掉。
export default {
name: 'keep-alive',// 组件名
abstract: true, // 抽象组件 就是不会被记录到 $children 和 $parent上
/**
include - 字符串或正则表达式。只有名称匹配的组件会被缓存。
exclude - 字符串或正则表达式。任何名称匹配的组件都不会被缓存。
max - 数字。最多可以缓存多少组件实例。
*/
props: {
include: patternTypes, // 可以缓存哪些组件 ['a','b']
exclude: patternTypes, // 可以排除哪些组件 ['a']
max: [String, Number] // 最大缓存个数 6
},
methods: {
cacheVNode() {
const { cache, keys, vnodeToCache, keyToCache } = this
if (vnodeToCache) {
const { tag, componentInstance, componentOptions } = vnodeToCache
cache[keyToCache] = { // 缓存中 防止需要缓存的组件 。 存放组件的实例
name: getComponentName(componentOptions),
tag,
componentInstance, // 组件实例渲染过会有$el 属性,下次可以直接复用$el属性
}
keys.push(keyToCache) // 放入key
// prune oldest entry // 超过最大长度会移除第一个
if (this.max && keys.length > parseInt(this.max)) {
pruneCacheEntry(cache, keys[0], keys, this._vnode)
}
this.vnodeToCache = null
}
}
},
created () {
this.cache = Object.create(null) // 弄了个缓存区 {}
this.keys = [] // 缓存组件的名字有哪些 []
},
destroyed () {
for (const key in this.cache) {
pruneCacheEntry(this.cache, key, this.keys)
}
},
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()
},
render () {
const slot = this.$slots.default // 取的是默认插槽
const vnode: VNode = getFirstComponentChild(slot) // 获取插槽中的第一个虚拟节点
// { Ctor, propsData, listeners, tag, children }
const componentOptions: ?VNodeComponentOptions = vnode && vnode.componentOptions
if (componentOptions) {
// check pattern
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
const key: ?string = vnode.key == null
// same constructor may get registered as different local components
// so cid alone is not enough (#3269)
? componentOptions.Ctor.cid + (componentOptions.tag ? `::${componentOptions.tag}` : '')
: vnode.key
if (cache[key]) { // 是否这个key缓存过
vnode.componentInstance = cache[key].componentInstance // 获取缓存的实例
// make current key freshest
remove(keys, key) // 把当前的key 作为最新的
keys.push(key)
} else {
// delay setting the cache until update
this.vnodeToCache = vnode // 缓存当前的vnode
this.keyToCache = key // 这个key也要缓存
}
vnode.data.keepAlive = true // 给虚拟节点增加了一个标识 data: keep-alive
}
// vnode上有data.keepAlive 和 我们的 componentInstance 说明这个虚拟节点是缓存过的
return vnode || (slot && slot[0]) // 最终返回vnode
}
}
小结
<keep-alive>缓存不活动的组件实例, 不会在函数式组件中正常工作,因为它们没有缓存实例。