vue2.0源码分析——内置组件

题目:vue2.0源码分析——keep-alive

前言:本篇文章,我将对vue2.0中所有的内置组件keep-alive的源码进行一个分析,我希望通过这篇文章的分析,不仅我自己也包括看这篇文章的所有小伙伴都能对vue的内置组件keep-alive都有一个较为深入的了解。那接下来我们就开始吧。
一、keep-alive

这个内置组件相信大家都很熟悉,这里要说一个比较丢脸的事情,就是笔者曾经第一次找工作面试时,问道了keep-alive的原理是什么,讲真,我是一脸懵逼,甚至用法我都有点忘了,所以那次面试也黄了,因此从那以后我下定决心,一定要把vue所有的内置组件原理全部弄明白,所以才有了今天这篇博客的诞生。

为了更加友好的对待使用vue不久的小伙伴,我们先来简单地回顾一下keep-alive的用法吧!

1.回顾用法
keep-alive用法原文链接
keep-alive接受三个参数

include exclude max
匹配可以被缓存的组件 匹配不可以被缓存的组件 最大缓存组件数
白名单 黑名单 上限

小结:keep-alive组件可以帮助我们缓存包裹在其中的组件,只要在白名单中,且没有超出限制,都会被缓存,也就意味着今后,被缓存的组件是不会被销毁的,也就是说正常情况下,被缓存组建的mounted之前的生命周期钩子函数将不会再执行。因为没有组件并没有被销毁,因此再次渲染的时候省去了生成虚拟DOM的环节,从而达到了性能优化的目的。

我相信通过这么一列举,小伙伴们都应该能够看的懂每个参数代表的含义了叭,但是我们今天不是来学用法的,而是去了解keep-alive这个内置组件的原理的。所以接下来我们要上源码啦!

2.分析源码
温馨提示:源码的位置是在5221行到5370行。不过由于版本的问题可能不准确,大家在源码文件中搜索keepalive找到应该会更靠谱一些!
vue2.0源码分析——内置组件_第1张图片
为了方便我们分析,来看一下源码到底做了什么?

var KeepAlive = {
    name: 'keep-alive',
    abstract: true,
    props: {
      include: patternTypes,
      exclude: patternTypes,
      max: [String, Number]
    },

    methods: {
      cacheVNode: function cacheVNode () {
        var ref = this;
        var cache = ref.cache;
        var keys = ref.keys;
        var vnodeToCache = ref.vnodeToCache;
        var keyToCache = ref.keyToCache;
        if (vnodeToCache) {
          var tag = vnodeToCache.tag;
          var componentInstance = vnodeToCache.componentInstance;
          var componentOptions = vnodeToCache.componentOptions;
          cache[keyToCache] = {
            name: getComponentName(componentOptions),
            tag: tag,
            componentInstance: componentInstance,
          };
          keys.push(keyToCache);
          // prune oldest entry
          if (this.max && keys.length > parseInt(this.max)) {
            pruneCacheEntry(cache, keys[0], keys, this._vnode);
          }
          this.vnodeToCache = null;
        }
      }
    },

    created: function created () {
      this.cache = Object.create(null);
      this.keys = [];
    },

    destroyed: function destroyed () {
      for (var key in this.cache) {
        pruneCacheEntry(this.cache, key, this.keys);
      }
    },

    mounted: function mounted () {
      var this$1 = this;
      this.cacheVNode();
      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); });
      });
    },

    updated: function updated () {
      this.cacheVNode();
    },

    render: function render () {
      var slot = this.$slots.default;  // 这个就是需要缓存的组件
      var vnode = getFirstComponentChild(slot); // 获取其虚拟DOM.
      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  // 不允许缓存的条件 走到这里就返回了,keep-alive是不会做缓存处理的。
        }

        var ref$1 = this;
        var cache = ref$1.cache;
        var keys = ref$1.keys;
        var key = vnode.key == null 
          // 总之在这里找一个唯一的值最为key
          ? componentOptions.Ctor.cid + (componentOptions.tag ? ("::" + (componentOptions.tag)) : '')
          : vnode.key;
        if (cache[key]) {
          vnode.componentInstance = cache[key].componentInstance;
          // 这个属于一个栈结构,让最访问最多了排在最前面,加大效率。
          remove(keys, key);
          keys.push(key);
        } else {
          // delay setting the cache until update
          this.vnodeToCache = vnode;
          this.keyToCache = key;
        }

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

因为keepalive本质上也是个组件,所以也和我们平时写组件一样需要书写配置项的,只是这个组件写的比较精妙,仅此而已。

好了,我们开始分析这个组件,接受3个参数,这个比较简单,我就不啰嗦了。

在created时,它给自己的data上新增了两个响应式变量
cache : { } 专门用来保存所缓存的组件
keys : [ ] 被缓存的所有组件的id所组成的数组

其次methods里面只有一个方法,其实就是往缓存对象里面放key , value的方法。但是在这里它加了一个判断,那就是只有当前实例对象有准备放入的vnode时,才进行放进盒子的操作。

看这个组件的时候要思考一个问题,一个vue实例组件,是先执行render函数呢,还是先执行mounted方法呢?思考一下?

答案是先执行render方法,因为mounted的意思是挂载完毕,意味着DOM已经生成了,那也就意味着肯定要执行了render函数DOM才能生成呀,所以我们先看这个组件的render函数,再分析mounted里面的内容。


其实里面的内容并不难,就是拿到被自己包裹的组件信息,包括名称,key,虚拟DOM等,如果不符合条件,直接返回虚拟DOM,也就意味着不会缓存,如果符合条件,就看一看当前缓存中有没有相应的key的组件,如果有,就直接返回缓存中的虚拟DOM

这个地方有个小技巧,就是它会尽可能的将最常访问的排在前面,这样可以减少遍历次数。

然后如果缓存中没有,就将当前的可缓存变量和可缓存key标位为可以。既赋值。告诉当前组件,我有需要缓存的组件。


然后render执行完之后,就到了mounted了,这里面首先会对exculde和inculde做一个监听,然后执行放入盒子的方法,然后因为之前已经告知组件我有需要缓存的内容,所以就缓存成功。

当下一次被包裹的内容变化,实际上意味着触发update方法,然后又回按照我们刚刚的流程走一遍,如此循环,这就是keepalive的原理。

3.重要概念
因此我们可以这样来理解keep-alive组件,本质上这其实还是一个组件,只不过这个组件并不渲染任何自己的内容,它渲染的都是被包裹的内容,并且内部维护了一个存放缓存的缓存器,被包裹的组件在符合条件的情况下,会被缓存到这个缓存器当中,只要第一次被缓存时过了,下一次就不需要再次生成,而是直接从缓存中读取。

你可能感兴趣的:(源码,javascript,vue.js,javascript)