keep-alive是vue.js的一个内置组件,它将不活动的组件保存在内存中,而非只讲将其销毁,提供了include(缓存),exclude(不缓存)两个属性,可以有选择的保存组件
语法:
<keep-alive> <component></component> </keep-alive>
生命钩子
keep-alive提供了两个生命钩子,activated,deactivated,用于确定组件是否处于活动状态
原理:
created和destoryed钩子
created会创建一个cache,用来做缓存容器,保存vnode节点
created () { /* 缓存对象 */ this.cache = Object.create(null) },
destoryed则在组件被销毁时清除cache里的组件实例
/* destroyed钩子中销毁所有cache中的组件实例 */ destroyed () { for (const key in this.cache) { pruneCacheEntry(this.cache[key]) } },
render钩子
- 首先获取插槽里的内容,
- 然后调用getFirstComponentChild获取第一个子组件,如果该组件有name,就用name,没有就用tag
- 用获取到的name和include,exclude做匹配,如果匹配失败则表示不缓存该组件,直接返回这个组件的vnode,成功则进如下一步缓存
缓存机制:
- 用获取的name去this.cache中寻找是否有该值,如果有则表示该组件有缓存(命中缓存),此时会直接从vnode中拿组件实例,调整key的顺序将其放到最后
- 如果没有命中缓存,则表示该组件还没有被缓存过,那么会把该组件存到cache中,如果此时超过了cache的最大缓存长度,会把第一个组件实例删除
完整render:
render() { /* 获取默认插槽中的第一个组件节点 */ const slot = this.$slots.default const vnode = getFirstComponentChild(slot) /* 获取该组件节点的componentOptions */ const componentOptions = vnode && vnode.componentOptions if (componentOptions) { /* 获取该组件节点的名称,优先获取组件的name字段,如果name不存在则获取组件的tag */ const name = getComponentName(componentOptions) const { include, exclude } = this /* 如果name不在inlcude中或者存在于exlude中则表示不缓存,直接返回vnode */ if ( (include && (!name || !matches(include, name))) || // excluded (exclude && name && matches(exclude, name)) ) { return vnode } const { cache, keys } = this const key = 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]) { vnode.componentInstance = cache[key].componentInstance // make current key freshest remove(keys, key) keys.push(key) } else { cache[key] = vnode keys.push(key) // prune oldest entry if (this.max && keys.length > parseInt(this.max)) { pruneCacheEntry(cache, keys[0], keys, this._vnode) } } vnode.data.keepAlive = true } return vnode || (slot && slot[0]) }
mounted钩子
调用了pruneCache方法用来观察include,exclude的变化
如果include
或exclude
发生了变化,即表示定义需要缓存的组件的规则或者不需要缓存的组件的规则发生了变化,那么就执行pruneCache
函数
function pruneCache (keepAliveInstance, filter) { const { cache, keys, _vnode } = keepAliveInstance for (const key in cache) { const cachedNode = cache[key] if (cachedNode) { const name = getComponentName(cachedNode.componentOptions) if (name && !filter(name)) { pruneCacheEntry(cache, key, keys, _vnode) } } } }
在该函数内对this.cache
对象进行遍历,取出每一项的name
值,用其与新的缓存规则进行匹配,如果匹配不上,则表示在新的缓存规则下该组件已经不需要被缓存,则调用pruneCacheEntry
函数将其从this.cache
对象剔除即可。
组件一旦被 <keep-alive> 缓存,那么再次渲染的时候就不会执行 created、mounted 等钩子函数。使用keepalive组件后,被缓存的组件生命周期会多activated和deactivated 两个钩子函数,它们的执行时机分别是 <keep-alive> 包裹的组件激活时调用和停用时调用。
完整的代码:
export default { name: 'keep-alive', abstract: true, props: { include: [String, RegExp, Array], exclude: [String, RegExp, Array], max: [String, Number] }, created () { this.cache = Object.create(null) this.keys = [] }, destroyed () { for (const key in this.cache) { pruneCacheEntry(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 = getFirstComponentChild(slot) /* 获取该组件节点的componentOptions */ const componentOptions = vnode && vnode.componentOptions if (componentOptions) { /* 获取该组件节点的名称,优先获取组件的name字段,如果name不存在则获取组件的tag */ const name = getComponentName(componentOptions) const { include, exclude } = this /* 如果name不在inlcude中或者存在于exlude中则表示不缓存,直接返回vnode */ if ( (include && (!name || !matches(include, name))) || // excluded (exclude && name && matches(exclude, name)) ) { return vnode } const { cache, keys } = this const key = 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]) { vnode.componentInstance = cache[key].componentInstance // make current key freshest remove(keys, key) keys.push(key) } else { cache[key] = vnode keys.push(key) // prune oldest entry if (this.max && keys.length > parseInt(this.max)) { pruneCacheEntry(cache, keys[0], keys, this._vnode) } } vnode.data.keepAlive = true } return vnode || (slot && slot[0]) } }
标签:Vue,name,cache,alive,vnode,缓存,key,组件,keep From: https://www.cnblogs.com/qianduan-Wu/p/16724928.html