vue版本: 2.6+
---
cache.111 中存放组件的缓存数据
注意1:exclude 的优先级高于 include,也就是说如果 include,exclude 都包含某个 name,将不会缓存该name对应的组件;
注意2:activated 钩子是在被缓存组件内部所有的子组件mounted执行之后调用,为了避免activated获取到错误的组件实例;
注意3:如果不写 name, 可以给组件名设置规则以 cached- 开头的组件会被缓存;
// 缓存以 cache- 开头的组件
...
渲染keep-alive先触发他的render,所以从render函数看起,为了好看可以粘贴到vscode中看
/* @flow */
import { isRegExp, remove } from 'shared/util'
import { getFirstComponentChild } from 'core/vdom/helpers/index'
type CacheEntry = {
name: ?string;
tag: ?string;
componentInstance: Component;
};
type CacheEntryMap = { [key: string]: ?CacheEntry };
function getComponentName (opts: ?VNodeComponentOptions): ?string {
return opts && (opts.Ctor.options.name || opts.tag)
}
function matches (pattern: string | RegExp | Array, 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
}
// 删除不满足filter的缓存
function pruneCache (keepAliveInstance: any, filter: Function) {
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)
}
}
}
}
function pruneCacheEntry (
cache: CacheEntryMap,
key: string,
keys: Array,
current?: VNode
) {
const entry: ?CacheEntry = cache[key]
if (entry && (!current || entry.tag !== current.tag)) {
entry.componentInstance.$destroy()
}
cache[key] = null
remove(keys, key)
}
const patternTypes: Array = [String, RegExp, Array]
export default {
name: 'keep-alive',
abstract: true,
props: {
include: patternTypes,
exclude: patternTypes,
max: [String, Number]
},
methods: {
// 设置keys,cache, 超过max的要把第一个删掉,每次update只会更新一个缓存,所以删除一个就够了
cacheVNode() {
const { cache, keys, vnodeToCache, keyToCache } = this
if (vnodeToCache) {
const { tag, componentInstance, componentOptions } = vnodeToCache
cache[keyToCache] = {
name: getComponentName(componentOptions),
tag,
componentInstance,
}
keys.push(keyToCache)
// prune oldest entry
if (this.max && keys.length > parseInt(this.max)) {
pruneCacheEntry(cache, keys[0], keys, this._vnode)
}
this.vnodeToCache = null
}
}
},
created () {
this.cache = Object.create(null)
this.keys = []
},
destroyed () {
// 销毁keep-alive清楚所有的缓存
for (const key in this.cache) {
pruneCacheEntry(this.cache, key, this.keys)
}
},
mounted () {
// 设置keys,cache, 超过max的要把第一个删掉,每次update只会更新一个缓存,所以删除一个就够了
this.cacheVNode()
// i中有about,在e中加上about,删会删除about
// e中有about,include中加上about,也不会增加about的缓存
// i,e都没有,在exclude加上会删除about的缓存
// 综上所述exclude的优先级比include高
// 删除不在include中的缓存 about
this.$watch('include', val => {
pruneCache(this, name => matches(val, name))
})
// 删除在exclude中的缓存 about
this.$watch('exclude', val => {
pruneCache(this, name => !matches(val, name))
})
},
updated () {
this.cacheVNode()
},
render () {
const slot = this.$slots.default
// 获取第一个子组件的vnode
const vnode: VNode = getFirstComponentChild(slot)
const componentOptions: ?VNodeComponentOptions = vnode && vnode.componentOptions
if (componentOptions) {
// check pattern
const name: ?string = getComponentName(componentOptions)
const { include, exclude } = this
if (
// not included
// include存在 且 name不存在或include不包含该name
(include && (!name || !matches(include, name))) ||
// excluded
// exclude 存在 且name存在而且在exclude中
(exclude && name && matches(exclude, name))
) {
// 直接返回vnode不做缓存
return vnode
}
// 否则缓存
const { cache, keys } = this
const key: ?string = vnode.key == null
// 相同的构造函数可能创建不同的本地组件,
// 所以仅仅是cid是不够的
// function getComponentName (opts: ?VNodeComponentOptions): ?string {
// return opts && (opts.Ctor.options.name || opts.tag)
// 组件的tag也有可能是undefined,这个暂时未知
// }
// 所以key的规则是,1. 优先是key属性值,然后是2. cid 或是 3. cid::tag
// 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
// 如果该组件已经有缓存把数据换上去,把key挪到keys的最后面,因为max是从前面开始删除的
if (cache[key]) {
vnode.componentInstance = cache[key].componentInstance
// make current key freshest
remove(keys, key)
keys.push(key)
} else {
// 否则延迟设置缓存直到update钩子触发, 应该是子组件的依赖更新会触发keepAlive的updated
// delay setting the cache until update
this.vnodeToCache = vnode
this.keyToCache = key
}
// 设置被缓存的组件的data.keepAlive为true
vnode.data.keepAlive = true
}
return vnode || (slot && slot[0])
}
}