keep-alive
是什么?
<keep-alive>
是 Vue
的内置组件,用来包裹组件,达到缓存组件实例的作用~
Vue
官方文档中提到:
<keep-alive>
是一个抽象组件:它自身不会渲染一个DOM
元素,也不会出现在组件的父组件链中。- 当组件在
<keep-alive>
内被切换,它的activated
和deactivated
这两个生命周期钩子函数将会被对应执行。
keep-alive 组件原理
匹配和过滤
include
- 字符串或正则表达式。只有名称匹配的组件会被缓存。exclude
- 字符串或正则表达式。任何名称匹配的组件都不会被缓存。
初始化
created
阶段会初始化两个变量,分别为cache
和keys
。mounted
阶段会对include
和exclude
变量的值做监测。
created() {
this.cache = Object.create(null)
this.keys = []
},
mounted() {
this.$watch('include', val => {
pruneCache(this, name => matches(val, name))
})
this.$watch('exclude', val => {
pruneCache(this, name => !matches(val, name))
})
},
可以看到,如果 include
或者 exclude
的值发生变化,就会触发 pruneCache
函数,不过筛选的条件需要根据 matches
函数的返回值来决定,所以我们先来看看它👇。
过滤条件
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
}
可以看到,pattern
可以有三种取值,分别为:
string
RegExp
Array<string>
之后判断 name
是不是在 pattern
指定的值/范围内,最后再根据结果返回 true
或者 false
。
缓存剔除
无论是 include
还是 exclude
,他们的值都不是一成不变的,每当过滤条件改变,都需要从已有的缓存中「剔除」不符合条件的 key
。
function pruneCache (keepAliveInstance: any, filter: Function) {
const { cache, keys, _vnode } = keepAliveInstance
for (const key in cache) {
const cachedNode: ?VNode = cache[key]
if (cachedNode) {
const name: ?string = getComponentName(cachedNode.componentOptions)
if (name && !filter(name)) {
pruneCacheEntry(cache, key, keys, _vnode)
}
}
}
}
可以看到这里又调用了 pruneCacheEntry
来「剔除」不符合缓存条件的 key
👇
function pruneCacheEntry (
cache: VNodeCache,
key: string,
keys: Array<string>,
current?: VNode
) {
const cached = cache[key]
if (cached && (!current || cached.tag !== current.tag)) {
cached.componentInstance.$destroy()
}
cache[key] = null
remove(keys, key)
}
总结
Vue.js内部将DOM节点抽象成了一个个的VNode节点,keep-alive组件的缓存也是基于VNode节点的而不是直接存储DOM结构。它将满足条件(pruneCache与pruneCache)的组件在cache对象中缓存起来,在需要重新渲染的时候再将vnode节点从cache对象中取出并渲染。