0%

vue-keep-alive源码分析

vue-keep-alive 的源码分析如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
/* keep-alive.js */
/* Vue.js v2.6.11 */
/* @flow */

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)
}
// 匹配函数
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
}

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)
}
}
}
}

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()
}
//移除 key
cache[key] = null
remove(keys, key)
}

const patternTypes: Array<Function> = [String, RegExp, Array]

export default {
name: 'keep-alive',
abstract: true,

props: {
include: patternTypes, //include 和 exclude 格式可以为 字符串、正则表达式、数组形式的 name。 eg. include = "a,b" | "/a|b/" | "[a,b]"
exclude: patternTypes, //当 include 和 exclude 中包含相同的 name 时,exclude 的优先级高即组件实例不会被缓存
max: [String, Number] //最多可以缓存的组件实例,当超过设定的 max 后,最久不被访问的组件实例会被销毁
},

created () {
this.cache = Object.create(null) //被缓存的组件实例被保存在 cache 中
this.keys = [] // 保存被缓存组件实例的缓存标识
},

destroyed () {
for (const key in this.cache) { //当 keep-alive 组件被销毁时,销毁所有已缓存的组件实例
pruneCacheEntry(this.cache, key, this.keys)
}
},

mounted () {
//观察传递给 keep-alive 的 include 和 exclude 属性,当发生变化时,销毁不在 include 或 在 exclude 中的组件实例
this.$watch('include', val => {
pruneCache(this, name => matches(val, name))
})
this.$watch('exclude', val => {
pruneCache(this, name => !matches(val, name))
})
},

render () {
const slot = this.$slots.default // 获取 <keep-alive> 中的 vnode
const vnode: VNode = getFirstComponentChild(slot) //
const componentOptions: ?VNodeComponentOptions = vnode && vnode.componentOptions //获取 vnode 选项
if (componentOptions) {
// check pattern
const name: ?string = getComponentName(componentOptions) //从 vnode 选项中获取组件名
const { include, exclude } = this
//如果组件不在 include 中 或 在 exclude 中 直接返回该 vnode
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)
// vnode 的 key 为空的情况下, 使用组件的构造器 id 和 组件的 tag 生成缓存标识
? componentOptions.Ctor.cid + (componentOptions.tag ? `::${componentOptions.tag}` : '')
: vnode.key
if (cache[key]) {
// 组件在缓存中存在,直接替换 vnode 的 componentInstance,并在 keys 中移除当前 key, 然后再 push, 这样做是为了当有 max 时,超出 max 时,方便移除 cache 中最不活跃的组件
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
//当设置 max 时,超出 max 后 移除最不活跃的组件
if (this.max && keys.length > parseInt(this.max)) {
pruneCacheEntry(cache, keys[0], keys, this._vnode)
}
}

vnode.data.keepAlive = true
}

return vnode || (slot && slot[0])
}
}

总结

<keep-alive> 的被动态切换时(通过 is 或 v-if ),如果目标组件未被缓存则对其进行缓存,如果该组件已被缓存则直接返回缓存的组件实例。