目录
keep-alive用法
源码
实现思路
LRU 缓存策略
abstract
1. 路由
2.动态组件
1.render(), 获取子组件虚拟DOM,vnode,此时的vnode并没有组件实例 componentInstance,首次渲染子组件,记录vnode(使用render创建组件实例),this.vnodeToCache = vnode;非首次渲染,已缓存,把缓存的组件实例赋值到当前vnode上,调整该组件访问的顺序;
2.mounted周期,执行cacheVNode,把首次渲染的组件虚拟DOM缓存起来
3.updated周期,当keep-alive的组件内容发生变化时,例如动态组件切换,执行cacheVNode缓存新组件
function _getComponentName(opts) {
return opts && (getComponentName(opts.Ctor.options) || opts.tag);
}
function matches(pattern, name) {
if (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);
}
return false;
}
function pruneCache(keepAliveInstance, filter) {
var cache = keepAliveInstance.cache, keys = keepAliveInstance.keys, _vnode = keepAliveInstance._vnode;
for (var key in cache) {
var entry = cache[key];
if (entry) {
var name_1 = entry.name;
if (name_1 && !filter(name_1)) {
pruneCacheEntry(cache, key, keys, _vnode);
}
}
}
}
function pruneCacheEntry(cache, key, keys, current) {
var entry = cache[key];
if (entry && (!current || entry.tag !== current.tag)) {
// @ts-expect-error can be undefined
entry.componentInstance.$destroy();
}
cache[key] = null;
remove$2(keys, key);
}
var patternTypes = [String, RegExp, Array];
var KeepAlive = {
name: 'keep-alive',
abstract: true, // TODO 标记为抽象组件
props: {
include: patternTypes,
exclude: patternTypes,
max: [String, Number]
},
methods: {
cacheVNode: function () {
/*
keyToCache: 7::A ,当前组件的缓存id
keys: ['7::A', '9::B'], 已缓存的组件id数组
cache: { key: {tag, name, componentInstance} }
vnodeToCache: vnode,虚拟DOM
*/
var _a = this, cache = _a.cache, keys = _a.keys, vnodeToCache = _a.vnodeToCache, keyToCache = _a.keyToCache;
console.log("this", this)
// 把子组件写入缓存,只写入一次,所以写入后置 this.vnodeToCache = null;
if (vnodeToCache) {
var tag = vnodeToCache.tag, componentInstance = vnodeToCache.componentInstance, componentOptions = vnodeToCache.componentOptions;
cache[keyToCache] = {
name: _getComponentName(componentOptions),
tag: tag,
componentInstance: componentInstance
};
keys.push(keyToCache);
// 移除访问时间离现在最久的组件 - keys[0]
if (this.max && keys.length > parseInt(this.max)) {
pruneCacheEntry(cache, keys[0], keys, this._vnode);
}
this.vnodeToCache = null;
}
}
},
// 创建缓存对象
created: function () {
this.cache = Object.create(null);
this.keys = [];
},
// 销毁缓存组件
destroyed: function () {
for (var key in this.cache) {
pruneCacheEntry(this.cache, key, this.keys);
}
},
// 写入缓存;监听 include、exclude需要缓存的组件
mounted: function () {
var _this = this;
this.cacheVNode();
this.$watch('include', function (val) {
pruneCache(_this, function (name) { return matches(val, name); });
});
this.$watch('exclude', function (val) {
pruneCache(_this, function (name) { return !matches(val, name); });
});
},
// 改变keep-alive内容时执行
updated: function () {
this.cacheVNode();
},
// 渲染keep-alive组件
render: function () {
var slot = this.$slots.default;
// 获取第一个子元素(keep-alive只允许有一个子元素)
var vnode = getFirstComponentChild(slot);
var componentOptions = vnode && vnode.componentOptions;
if (componentOptions) {
// check pattern
var name_2 = _getComponentName(componentOptions);
var _a = this, include = _a.include, exclude = _a.exclude;
// 没在缓存名单中,直接渲染子组件
if (
// not included
(include && (!name_2 || !matches(include, name_2))) ||
// excluded
(exclude && name_2 && matches(exclude, name_2))) {
return vnode;
}
// 在缓存名单中
var _b = this, cache = _b.cache, keys = _b.keys;
var key = vnode.key == null
? componentOptions.Ctor.cid +
(componentOptions.tag ? "::".concat(componentOptions.tag) : '')
: vnode.key;
// 子组件非首次访问,已缓存,调整该组件访问的顺序, keys.push(key)表示最新访问的组件
if (cache[key]) {
vnode.componentInstance = cache[key].componentInstance;
// make current key freshest
remove$2(keys, key);
keys.push(key);
}
else {
// 子组件首次访问,记录vnode
this.vnodeToCache = vnode;
this.keyToCache = key;
}
// @ts-expect-error can vnode.data can be undefined
vnode.data.keepAlive = true;
}
return vnode || (slot && slot[0]);
}
};
子组件非首次渲染时,要调整该组件访问的顺序,调整为最近访问组件。当缓存组件>max数量时,删除最久未访问的组件。
//catch
render: function () {
var slot = this.$slots.default;
var vnode = getFirstComponentChild(slot);
var componentOptions = vnode && vnode.componentOptions;
if (componentOptions) {
......
// 子组件非首次访问,调整该组件访问的顺序, keys.push(key)表示最新访问的组件
if (cache[key]) {
vnode.componentInstance = cache[key].componentInstance;
remove$2(keys, key);
keys.push(key);
}
......
}
return vnode || (slot && slot[0]);
}
//删除
cacheVNode: function () {
var _a = this, cache = _a.cache, keys = _a.keys, vnodeToCache = _a.vnodeToCache, keyToCache = _a.keyToCache;
// 把子组件写入缓存,只写入一次,所以写入后置 this.vnodeToCache = null;
if (vnodeToCache) {
......
keys.push(keyToCache);
// 移除访问时间离现在最久的组件 - keys[0]
if (this.max && keys.length > parseInt(this.max)) {
pruneCacheEntry(cache, keys[0], keys, this._vnode);
}
......
}
}
表示该组件未抽象组件,不渲染在Dom树中