一、抽象组件
从vue源码中可以知道, 将组件的abstract属性设置为true, 这个组件将变成抽象组件, 既为抽象组件, 顾名思义,即是抽象的, 不生成实际的dom结构的, 那抽象组件有什么作用呢? 通过keep-alive组件的设计, 便可窥其一二.
二、keep-alive 源码分析
// 组件的结构和平常写的组件并无差异
export default {
name: 'keep-alive',
abstract: true, // 设置为抽象组件
props: {
// 属性:include, 匹配到的组件会被缓存
include: patternTypes,
// 属性: exclude, 匹配到的组件不会被缓存
exclude: patternTypes,
// 属性: max, 最大缓存个数
max: [String, Number],
},
created() {
// 初始化
this.cache = Object.create(null);
this.keys = [];
},
destroyed() {
for (const key in this.cache) {
pruneCacheEntry(this.cache, key, this.keys);
}
},
mounted() {
this.$watch('include', val => {
pruneCache(this, name => matches(val, name));
});
this.$watch('exclude', val => {
pruneCache(this, name => !matches(val, name));
});
},
// 使用render函数
render() {
// 拿到默认插槽数组
const slot = this.$slots.default;
// 取第一个子组件
const vnode: VNode = getFirstComponentChild(slot);
// 拿到子组件options
const componentOptions: ?VNodeComponentOptions = vnode && vnode.componentOptions;
if (componentOptions) {
// 拿到子组件的name
const name: ?string = getComponentName(componentOptions);
const { include, exclude } = this;
// 不存在name或者不在included或者在exclude中直接返回子组件的vNode
if (
(include && (!name || !matches(include, name))) ||
(exclude && name && matches(exclude, name))
) {
return vnode;
}
const { cache, keys } = this;
// 解决#3629 issue, 确保key的唯一性
const key: ?string =
vnode.key == null
? componentOptions.Ctor.cid +
(componentOptions.tag ? `::${componentOptions.tag}` : '')
: vnode.key;
// 存在, 则将子组件的vNode.componentInstance指向缓存
if (cache[key]) {
vnode.componentInstance = cache[key].componentInstance;
// 保持key在最后,这里涉及到max设置之后的舍弃缓存组件的问题
remove(keys, key);
keys.push(key);
// 不存在, 则将vNode推入cache
} else {
cache[key] = vnode;
keys.push(key);
// 如果设置了最大缓存个数, 并超出了最大缓存个数, 清除cache数组的第一个组件
if (this.max && keys.length > parseInt(this.max)) {
pruneCacheEntry(cache, keys[0], keys, this._vnode);
}
}
// 设置keepAlive为true, 为以后做铺垫
vnode.data.keepAlive = true;
}
// 返回子组件
return vnode || (slot && slot[0]);
},
};
从上可以知道, 抽象组件是可以对子组件进行统一加工的一类组件, 可以在抽象组件中操作子组件.