是Vue
中内置的一个抽象组件,它自身不会渲染一个 DOM
元素,也不会出现在父组件链中。当它包裹动态组件时,会缓存不活动的组件实例,而不是销毁它们。
对于一些不需要重复渲染页面的场景,例如从列表进入详情,再次返回时该页面保持原因的状态,这时候就用到了keep-alive,它的作用就是用于保存组件的渲染状态。
在动态组件中的应用:
在vue-router中的应用
该组件接收三个参数:第一个是生命需要缓存的组件名,第二个是不需要缓存的组件名,第三个是最多可以缓存多少个组件。
props: {
include: [String, RegExp, Array],
exclude: [String, RegExp, Array],
max: [String, Number]
}
mounted () {
this.$watch('include', val => {
pruneCache(this, name => matches(val, name))
})
this.$watch('exclude', val => {
pruneCache(this, name => !matches(val, name))
})
}
在props中接受了外面传的三个参数,同时在 mounted 中监听了前两个参数的变化,这两个参数的变化意味着需要缓存的组件的规则或者不需要缓存的组件的规则发生了变化,这个时候就会执行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)
}
}
}
}
function pruneCacheEntry (cache,key,keys,current) {
const cached = cache[key]
if (cached && (!current || cached.tag !== current.tag)) {
cached.componentInstance.$destroy()
}
cache[key] = null
remove(keys, key)
}
函数的作用简单理解就是对每个组件的key进行了缓存,随着规则的不断变化,每次进入都会将需要缓存的组件记录下来,不需要缓存的则销毁掉。至于被记录下来的这个组件,究竟是怎么实现每次进入都不会重新渲染的,继续往下看,这就需要另外一个重要的函数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])
}
render所实现的大致功能是:首先获取keep-alive中包含组件的名称,然后和keep-alive定义的是否需要缓存的组件规则去配置;如果不需要缓存,则直接返回这个组件的vnode节点,否则的话则进行缓存,当下一次进入时候,再直接从缓存里取出来。
因为vue框架本身虚拟dom的属性,所以组件实例都是以JS的形式描绘出来的,只要该组件的数据模型层没有修改,它是不会重新渲染的。总结就是组件一旦被keep-alive缓存,那么再次执行的时候就不会执行created、mounted等钩子函数。
但实际场景中有可能还会多长另外一个需求,如前面的例子所讲,从详情返回列表时,视图上不需要做任何更新停留在原有的状态,但同时又需要触发另外一些逻辑,也就是希望在我们被缓存的组件再次被渲染的时候做一些事情,好在vue提供了activated和deactivated两个钩子函数,而且也是keep-alive独有的,也就是说任何组件只有在被keep-alive包裹的情况下,才会执行这两个函数。