keep-alive缓存机制

15 篇文章 0 订阅

目录

keep-alive用法

源码

实现思路

LRU 缓存策略

abstract


keep-alive用法

1. 路由

<keep-alive>
    <router-view></router-view>
</keep-alive>

2.动态组件

<keep-alive>
   <component :is="active"></component>
</keep-alive>

源码

实现思路

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

LRU 缓存策略

子组件非首次渲染时,要调整该组件访问的顺序,调整为最近访问组件。当缓存组件>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);
    }
    ......
  }
}

abstract

表示该组件未抽象组件,不渲染在Dom树中

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值