很简单,我们直接看vue的源码即可。
keep-alive组件支持三个属性。

props: {
    include: patternTypes,
    exclude: patternTypes,
    max: [String, Number]
},

其中patternTypes = [String, RegExp, Array];
组件在created周期时初始化自己的记录集合

created: function created () {
    this.cache = Object.create(null); // 以组件实例的 key和vnode作为key-value存入其中
    this.keys = []; // 存储组件实例的 key
},

mounted周期里面开始注册自己的watch,watch自己的includeexclude属性进行pruneCache

mounted: function mounted () {
    var this$1 = this;

    this.$watch('include', function (val) {
      pruneCache(this$1, function (name) { return matches(val, name); });
    });
    this.$watch('exclude', function (val) {
      pruneCache(this$1, function (name) { return !matches(val, name); });
    });
},

里面的几个辅助函数主要是用来比较组件是否需要缓存和操作缓存的。

function getComponentName (opts) {
  return opts && (opts.Ctor.options.name || opts.tag) // 获取组件的名字或者tag
}

function matches (pattern, name) { // 支持多种格式的匹配
  if (Array.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)
  }
  /* istanbul ignore next */
  return false
}

function pruneCache (keepAliveInstance, filter) {
  var cache = keepAliveInstance.cache; // 在keep-alive组件created时创建的
  var keys = keepAliveInstance.keys; // 同上
  var _vnode = keepAliveInstance._vnode;
  for (var key in cache) {
    var cachedNode = cache[key];
    if (cachedNode) {
      var name = getComponentName(cachedNode.componentOptions);
      if (name && !filter(name)) {
        pruneCacheEntry(cache, key, keys, _vnode);//  对于不符合include或exclude规则的组件实例调整对应的cache
      }
    }
  }
}

function pruneCacheEntry (
  cache,
  key,
  keys,
  current
) {
  var cached$$1 = cache[key];
  if (cached$$1 && (!current || cached$$1.tag !== current.tag)) {
    cached$$1.componentInstance.$destroy();
  }
  cache[key] = null; // 在cache对象和keys数组里面同步删除该key
  remove(keys, key);
}

复杂点的部分就是render

render: function render () {
    var slot = this.$slots.default;
    var vnode = getFirstComponentChild(slot);
    var componentOptions = vnode && vnode.componentOptions;
    if (componentOptions) {
      // check pattern
      var name = getComponentName(componentOptions);
      var ref = this;
      var include = ref.include;
      var exclude = ref.exclude;
      if (
        // not included
        (include && (!name || !matches(include, name))) ||
        // excluded
        (exclude && name && matches(exclude, name))
      ) {
        return vnode
      }

      var ref$1 = this;
      var cache = ref$1.cache;
      var keys = ref$1.keys;
      var key = vnode.key == null
        // same constructor may get registered as different local components
        // so cid alone is not enough (#3269)
        ? componentOptions.Ctor.cid + (componentOptions.tag ? ("::" + (componentOptions.tag)) : '')
        : vnode.key;
      if (cache[key]) {
        vnode.componentInstance = cache[key].componentInstance; // 直接使用缓存里面的组件实例
        // make current key freshest // 在原位置移除再重新推入,确保key在keys最后面(即最新的)
        remove(keys, key);
        keys.push(key);
      } else {
        cache[key] = vnode;
        keys.push(key);
        // prune oldest entry
        if (this.max && keys.length > parseInt(this.max)) {
          pruneCacheEntry(cache, keys[0], keys, this._vnode);
        }
      }

      vnode.data.keepAlive = true;
    }
    return vnode || (slot && slot[0])
}

0 条评论

发表回复

Avatar placeholder