抽象组件-keep-alive

101 阅读1分钟

一、抽象组件

从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]);
    },
};

从上可以知道, 抽象组件是可以对子组件进行统一加工的一类组件, 可以在抽象组件中操作子组件.