1.keep-alive 组件的 props:

  • include - 字符串或正则表达式。只有名称匹配的组件会被缓存。
  • exclude - 字符串或正则表达式。任何名称匹配的组件都不会被缓存。
  • max - 数字。最多能够缓存多少组件实例。

    2.keep-alive 是 vue 的内建组件,所有组件能够通过 components 获取 keep-alive 组件选项(内建组件是以原型的模式保留在 components 对象上,而不是间接保留在 components 上),依据获取到的组件选项创立 keep-alive 组件。

// keep-alive组件选项var KeepAlive = {    name: 'keep-alive',    abstract: true, // 形象组件    props: {        ...    },    created: function created() {        this.cache = Object.create(null);// 缓存组件        this.keys = [];// 缓存组件标识    },    mouted: {        ...    },    render: {        ...    }};

3.keep-alive 组件渲染页面调用 render 函数时,先获取 keep-alive 组件标签内的组件标签节点(默认插槽生成的节点,VNode 实例)。再依据组件名称判断节点是否满足缓存的条件,如果不满足间接返回节点,否则判断该节点是否曾经缓存。如果曾经缓存,间接读取之前缓存的组件(Vue 实例),保留到以后节点上,而后返回以后节点。如果未缓存,则缓存以后节点后再返回。缓存的内容是组件,组件本身蕴含了数据状态,依据数据渲染页面,所以组件激活时可能放弃之前的状态。

// keep-alive 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))) || // include中没有name      // excluded      (exclude && name && matches(exclude, name)) // 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      remove(keys, key);      keys.push(key);    } else {      // 增加组件缓存      cache[key] = vnode;      keys.push(key);      // prune oldest entry      if (this.max && keys.length > parseInt(this.max)) {        // 超出最大数量限度,销毁最先换成的节点实例(max为字符串0时会报错)        pruneCacheEntry(cache, keys[0], keys, this._vnode);      }    }    vnode.data.keepAlive = true; // 组件缓存标识,保障组件不被销毁  }  return vnode || (slot && slot[0]);}

4.缓存组件激活(节点 DOM 插入到页面)时,调用activated组件生命周期函数。当缓存组件标签切换,组件节点销毁时,因为组件节点标识 keepAlive 为 true,并不会调用$destory 销毁组件,而是解冻组件并触发deactivated组件生命周期函数,同时将组件对应的DOM从文档中移除。

// 节点生命周期var componentVNodeHooks = {    ...    insert: function insert (vnode) {        var context = vnode.context;        var componentInstance = vnode.componentInstance;        if (!componentInstance._isMounted) {        componentInstance._isMounted = true;        callHook(componentInstance, 'mounted');// 插入到DOM后触发        }        if (vnode.data.keepAlive) {        if (context._isMounted) {            // vue-router#1212            // During updates, a kept-alive component's child components may            // change, so directly walking the tree here may call activated hooks            // on incorrect children. Instead we push them into a queue which will            // be processed after the whole patch process ended.            queueActivatedComponent(componentInstance);        } else {            activateChildComponent(componentInstance, true /* direct */);// 激活组件        }        }    },    ...    destroy: function destroy (vnode) {// 节点销毁时的生命周期函数        var componentInstance = vnode.componentInstance;        if (!componentInstance._isDestroyed) {        if (!vnode.data.keepAlive) {// 是不是keep-alive中的缓存组件            componentInstance.$destroy();        } else {            deactivateChildComponent(componentInstance, true /* direct */);        }        }    }};// 激活组件function activateChildComponent (vm, direct) {  if (direct) {    vm._directInactive = false;    if (isInInactiveTree(vm)) {      return    }  } else if (vm._directInactive) {    return  }  if (vm._inactive || vm._inactive === null) {    vm._inactive = false;    for (var i = 0; i < vm.$children.length; i++) {      activateChildComponent(vm.$children[i]);    }    callHook(vm, 'activated');  }}// 解冻组件function deactivateChildComponent (vm, direct) {  if (direct) {    vm._directInactive = true;    if (isInInactiveTree(vm)) {      return    }  }  if (!vm._inactive) {    vm._inactive = true;    for (var i = 0; i < vm.$children.length; i++) {      deactivateChildComponent(vm.$children[i]);// 解冻子组件    }    callHook(vm, 'deactivated');// 调用组件解冻生命周期函数  }}

5.keep-alive 是通过 include 和 exclude 判断组件是否缓存。在 keep-alive 组件 mounted 生命周期函数中,通过组件内置的&dollar;watch 办法监测 include 和 exclude 值的变动,当发生变化时,依据变动后的值判断以后曾经缓存的组件哪些不满足缓存条件,不满足缓存条件的革除组件缓存。通过 include 和 exclude 能够实现动静缓存,例如人员列表页面跳转到车辆列表页面不缓存,然而跳转到人员详情页缓存,就能够页面跳转前更改 include 或者 exclude 值,更新缓存条件,实现动静缓存。

// keep-alive propsprops: {    include: patternTypes,    exclude: patternTypes,    max: [String, Number],}
// keep-alive mouted生命周期函数mounted: function mounted() {  var this$1 = this;  this.$watch("include", function (val) {    // 监测include属性值变动    pruneCache(this$1, function (name) {      return matches(val, name);    }); // 当组件不满足缓存条件时销毁  });  this.$watch("exclude", function (val) {    // 监测exclude属性值变动    pruneCache(this$1, function (name) {      return !matches(val, name);    }); // 当组件不满足缓存条件时销毁  });}

6.keep-alive组件销毁时,同时也会销毁缓存的组件。

// keep-alive destroyed生命周期函数destroyed: function destroyed() {  for (var key in this.cache) {    pruneCacheEntry(this.cache, key, this.keys); // 销毁组件  }}