注:以下是个人理解、如有不对还望指正!
keep-alive
keep-alive主要用做组件内容缓存,当切换组件是不会对当前组件进行卸载。要是有`include、exclude、max`
三个属性;前两个属性允许`keep-alive`有条件的进行缓存;`max`可以定义组件最大的缓存个数,如果超过了这
个个数的话,使用LRU策略,策略根据数据的历史访问记录来进行淘汰数据。LRU 策略的设计原则是,如果一个
数据在最近一段时间没有被访问到,那么在将来它被访问的可能性也会小。就是说,当限定的空间已存满数据时,
应当把最长时间内没有被重复访问到的数据淘汰,在下次存储数据时将最长时间没有访问到的数据进行淘汰
源码分析
下面展示部分源码及其注释
- 获取组件的名称
function getComponentName (opts: ?VNodeComponentOptions): ?string {
return opts && (opts.Ctor.options.name || opts.tag)
}
// 判断是否匹配成功 根据 include exclude 判断
function matches (pattern: string | RegExp | Array<string>, name: string): boolean {
if (Array.isArray(pattern)) { // 判断是否为数组
return pattern.indexOf(name) > -1
} else if (typeof pattern === 'string') { // 判断是否为字符串
return pattern.split(',').indexOf(name) > -1
} else if (isRegExp(pattern)) { // 是否为正则
return pattern.test(name)
}
/* istanbul ignore next */
return false
}
- 修正缓存
// 当前 keep-alive 实例 缓存 key 数组 当前虚拟NODE
const { cache, keys, _vnode } = keepAliveInstance
// 对于不需要缓存的组件及时清除
for (const key in cache) {
const entry: ?CacheEntry = cache[key]
if (entry) {
const name: ?string = entry.name
if (name && !filter(name)) {
pruneCacheEntry(cache, key, keys, _vnode)
}
}
}
}
- 清除缓存
cache: CacheEntryMap,
key: string,
keys: Array<string>,
current?: VNode
) {
const entry: ?CacheEntry = cache[key]
// 如果缓存对象 没有实例或者 当前缓存的标签不等于当前标签
if (entry && (!current || entry.tag !== current.tag)) {
// 强制销毁
entry.componentInstance.$destroy()
}
// 清空
cache[key] = null
// 删除
remove(keys, key)
}
- render
// 获取插槽的默认元素 this.$slots
const slot = this.$slots.default
// 获取第一个缓存的组件
const vnode: VNode = getFirstComponentChild(slot)
// 拿出当前组件的 options 选项
const componentOptions: ?VNodeComponentOptions = vnode && vnode.componentOptions
if (componentOptions) {
// check pattern
// 获取组件名称
const name: ?string = getComponentName(componentOptions)
// 获取组件传过来的 props 包含 未包含
const { include, exclude } = this
if (
// not included
(include && (!name || !matches(include, name))) ||
// excluded
(exclude && name && matches(exclude, name))
) {
return vnode
}
const { cache, keys } = this
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]) {
vnode.componentInstance = cache[key].componentInstance
// make current key freshest
// LRU 缓存淘汰算法
remove(keys, key)
keys.push(key)
} else {
// delay setting the cache until update
// 缓存当前的组件实例
this.vnodeToCache = vnode
// 缓存当前组件的 key
this.keyToCache = key
}
vnode.data.keepAlive = true
}
// 返回组件实例 返回插槽第一个元素
return vnode || (slot && slot[0])
}
}
总结
`keep-alive` 组件是抽象组件,它只对包裹的子组件做处理,主要是根据LRU策略缓存组件 `VNode`,最后在
`render` 时返回子组件的 `VNode`。缓存渲染过程会更新 `keep-alive` 插槽,重新再 `render` 一次,
从缓存中读取之前的组件 `VNode` 实现状态缓存。