vue2 的 keepAlive 实现原理
# vue 的 keepAlive 实现
keepAlive 的实现简单来说就是通过拿到在 keepalive 中渲染的 vue 对象,并判断其是否需要被缓存,若需要则拿到这个 vue 对象内一些独一无二的值作为存储的键,并将该 vue 对象作为值存入缓存的对象中,再次访问时去缓存中查找是否有该对象,有的话就将其抛出,没有的话便渲染新的就完事了。是个人都能看懂所以直接就放源码了。
// type CacheEntry = {
// name: ?string;
// tag: ?string;
// componentInstance: Component;
// };
function isDef(v) {
return v !== undefined && v !== null;
}
function isAsyncPlaceholder(node) {
return node.isComment && node.asyncFactory;
}
function isRegExp(v) {
return Object.prototype.toString.call(v) === "[object RegExp]";
}
function remove(arr, item) {
if (arr.length) {
const index = arr.indexOf(item);
if (index > -1) {
return arr.splice(index, 1);
}
}
}
function getFirstComponentChild(children) {
if (Array.isArray(children)) {
for (let i = 0; i < children.length; i++) {
const c = children[i];
if (isDef(c) && (isDef(c.componentOptions) || isAsyncPlaceholder(c))) {
return c;
}
}
}
}
// type CacheEntryMap = { [key: string]: ?CacheEntry };
function matches(pattern /** Includes or Excludes */, name /** string */) {
if (Array.isArray(pattern)) {
return pattern.includes(name) > -1;
} else if (typeof pattern === "string") {
return pattern.split(",").indexOf(name) > -1;
} else if (isRegExp(pattern)) {
return pattern.test(name);
}
return false;
}
function getComponentName(opts) {
return opts && (opts.Ctor.name || opts.tag);
}
function pruneCache(keepAliveInstance /** this */, filterFn /** Function */) {
const { cache, keys, _vnode } = keepAliveInstance;
for (const key in cache) {
if (Object.hasOwnProperty.call(cache, key)) {
const entry = cache[key];
if (entry) {
const name = entry.name; // ''
// 如果名字存在但是并不在缓存数组内,则销毁并移除出缓存
if (name && !filterFn(name)) {
pruneCacheEntry(cache, key, keys, _vnode);
}
}
}
}
}
function pruneCacheEntry(cache, key, keys, current /** VNode */) {
const entry = cache[key];
if (entry && (!current || entry.tag !== current.tag)) {
entry.componentInstance.$destory(); // 销毁组件
}
cache[key] = null;
remove(keys, key);
}
export default {
name: "KeepAliveNext",
abstract: true, // 所有路由模型选项将被简化成一个单个的 mode 选项
props: {
includes: [String, RegExp, Array],
exclude: [String, RegExp, Array],
max: {
type: [String, Number],
default: 8, // 这里默认最多缓存8个页面
},
},
created() {
this.cache = Object.create(null);
this.keys = [];
},
mounted() {
console.log("mounted");
this.cacheVNode();
this.$watch("include", (val) => {
pruneCache(this, (name) => matches(val, name));
});
this.$watch("exclude", (val) => {
pruneCache(this, (name) => !matches(val, name));
});
},
updated() {
this.cacheVNode();
},
destroyed() {
// 销毁所有组件 清空缓存
for (const key in this.cache) {
pruneCacheEntry(this.cache, key, this.keys);
}
},
methods: {
cacheVNode() {
const { cache, keys, vnodeToCache, keyToCache } = this;
console.log(cache, keys, vnodeToCache, keyToCache);
if (vnodeToCache) {
const { tag, componentInstance, componentOptions } = vnodeToCache;
// CacheEntry
cache[keyToCache] = {
name: getComponentName(componentOptions),
tag,
componentInstance,
};
keys.push(keyToCache);
// 超出长度限制时把最先缓存的内容移除
if (this.max && keys.length > parseInt(this.max)) {
pruneCacheEntry(cache, keys[0], keys, this._vnode);
}
this.vnodeToCache = null;
}
},
},
render() {
const slot = this.$slot.default;
const vnode = getFirstComponentChild(slot); // 找到最外层第一个子组件vnode
const componentOptions = vnode && vnode.componentOptions;
if (componentOptions) {
const name = getComponentName(componentOptions);
const { include, exclude } = this;
if (
// 不存在includes里 或是存在excludes里时,返回新的vnode,否则从缓存中拿
(include && (!name || !matches(include, name))) ||
(exclude && name && matches(exclude, name))
) {
return vnode;
}
const { cache, keys } = this;
// key 可能是 undefined 或者 null ,js的bug == 时浅判断
const key =
vnode.key == null
? componentOptions.Ctor.cid +
(componentOptions.tag ? `::${componentOptions.tag}` : "")
: vnode.key;
if (cache[key]) {
vnode.componentInstance = cache[key].componentInstance;
remove(keys, key);
keys.push(key);
} else {
this.vnodeToCache = vnode;
this.keyToCache = key;
}
vnode.data.keepAlive = true;
}
return vnode || (slot && slot[0]);
},
};
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
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
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
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
编辑 (opens new window)
上次更新: 2023/03/27, 21:41:56