import { isRegExp, remove } from 'shared/util'
import { getFirstComponentChild } from 'core/vdom/helpers/index'
type VNodeCache = { [key: string]: ?VNode };
// 獲取組件名
function getComponentName (opts: ?VNodeComponentOptions): ?string {
return opts && (opts.Ctor.options.name || opts.tag)
}
// 一個檢測name是否匹配的函數
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
}
// 修正cache
function pruneCache (keepAliveInstance: any, filter: Function) {
const { cache, keys, _vnode } = keepAliveInstance
for (const key in cache) {
// 取出cache中的vnode
const cachedNode: ?VNode = cache[key]
if (cachedNode) {
const name: ?string = getComponentName(cachedNode.componentOptions)
/* name不符合filter條件的,同時不是目前渲染的vnode時,銷燬vnode對應的組件實例(Vue實例),並從cache中移除 */
if (name && !filter(name)) {
pruneCacheEntry(cache, key, keys, _vnode)
}
}
}
}
function pruneCacheEntry (
cache: VNodeCache,
key: string,
keys: Array<string>,
current?: VNode
) {
const cached = cache[key]
if (cached && (!current || cached.tag !== current.tag)) {
/* 銷燬vnode對應的組件實例(Vue實例) */
cached.componentInstance.$destroy()
}
cache[key] = null
remove(keys, key)
}
const patternTypes: Array<Function> = [String, RegExp, Array]
export default {
name: 'keep-alive' ,
abstract: true ,
props: {
include: patternTypes,
exclude: patternTypes,
max: [String, Number]
},
created () {
/* 緩存對象 */
this .cache = Object.create( null )
this .keys = []
},
/* destroyed鉤子中銷燬所有cache中的組件實例 */
destroyed () {
for (const key in this .cache) {
pruneCacheEntry( this .cache, key, this .keys)
}
},
mounted () {
/* 監視include以及exclude,在被修改的時候對cache進行修正 */
this .$watch( 'include' , val => {
pruneCache( this , name => matches(val, name))
})
this .$watch( 'exclude' , val => {
pruneCache( this , name => !matches(val, name))
})
},
render () {
/* 得到slot插槽中的第一個組件 */
const slot = this .$slots. default
const vnode: VNode = getFirstComponentChild(slot)
const componentOptions: ?VNodeComponentOptions = vnode && vnode.componentOptions
if (componentOptions) {
// check pattern
/* 獲取組件名稱,優先獲取組件的name字段,否則是組件的tag */
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
/* 如果已經做過緩存了則直接從緩存中獲取組件實例給vnode,還未緩存過則進行緩存 */
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)
}
}
/* keepAlive標記位 */
vnode.data.keepAlive = true
}
return vnode || (slot && slot[0])
}
}
|