keep-alive用于组件缓存,保证我们在页面切换时,缓存部分组件,具体实现如下
1、keep-alive 是一个抽象组件
export default {
name: 'keep-alive',
abstract: true,------>标志抽象组件,不会生成标签之类的
props: {
include: patternTypes,
exclude: patternTypes,
max: [String, Number]
},
可以看到接收三个值 用于缓存组件的规则,同时接收缓存最大数
2、初始化阶段会做几件事情,创建空对象(用于缓存当前节点vnode),空数组,存放Key(类似于控制的队列)
created () {
this.cache = Object.create(null)
this.keys = []
},
3、接着在挂载阶段和卸载阶段分别对缓存对象进行监听和销毁
destroyed () {
for (const key in this.cache) {
pruneCacheEntry(this.cache, key, this.keys) ------>卸载
}
},
mounted () {
this.$watch('include', val => { ---------->通过此方式监听,命中规则的变化,进而达到
pruneCache(this, name => matches(val, name)) 动态改变 include、exclude值变化
}) 缓存组件
this.$watch('exclude', val => {
pruneCache(this, name => !matches(val, name))
})
},
4、最后一步是渲染组件,利用slot,获取到Keep-alive中组件信息(只能缓存一个组件),通过获取组件名字来和规则对比,决定是否需要缓存,最后为组件打上 KeepAlive =true 的标记,保证在渲染次组件时,不进行 created和mounted
render () {
const slot = this.$slots.default ---通过slot获取组件vnode
const vnode: VNode = getFirstComponentChild(slot) --------->故只能缓存第一个组件
const componentOptions: ?VNodeComponentOptions = vnode && vnode.componentOptions
if (componentOptions) {
// check pattern -----获取组件name
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 ---以下是 对超过缓存数清理
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
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])
}
几个处理函数
1、pruneCache,意为修剪缓存,可以理解为更新缓存的组件
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)
}
}
}
}
2、pruneCacheEntry 用于销毁
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)
}
3、keepAlive 在组件挂载时的作用
var componentVNodeHooks = {
init: function init (vnode, hydrating) {
if (
vnode.componentInstance &&
!vnode.componentInstance._isDestroyed &&
vnode.data.keepAlive
) {
// kept-alive components, treat as a patch
var mountedNode = vnode; // work around flow
componentVNodeHooks.prepatch(mountedNode, mountedNode); --------->初始化时
...
prepatch: function prepatch (oldVnode, vnode) {
var options = vnode.componentOptions;
var child = vnode.componentInstance = oldVnode.componentInstance;
updateChildComponent( -------------------->可理解为 打补丁更新,非重新渲染
child,
options.propsData, // updated props
options.listeners, // updated listeners
vnode, // new parent vnode
options.children // new children
);
},
只是用于自己理解,部分词汇非专业术语