美文网首页Vue
史上最全的vue.js源码解析(四)

史上最全的vue.js源码解析(四)

作者: Bonne_nuit | 来源:发表于2021-08-22 22:02 被阅读0次

    虽然vue3已经出来很久了,但我觉得vue.js的源码还是非常值得去学习一下。vue.js里面封装的很多工具类在我们平时工作项目中也会经常用到。所以我近期会对vue.js的源码进行解读,分享值得去学习的代码片段,这篇文章将会持续更新。

    一、4000~6200代码有哪些内容?:

    1.initState:初始化实例状态(initProps,initData,initComputed,initMethods,initWatch,initMixin等)
    2.initAssetRegisters:创建组件、指令、过滤器
    3.initGlobalAPI:初始化全局api
    4.renderClass,mergeClassData,stringifyClass:对 class 的转码、合并和其他二次封装的工具函数
    5.ref的注册和实现原理(ref是给元素或者子组件注册引用信息的)

    二.4000~6200行代码的重点:

    1.Watcher(重点)
    在 Watcher 的原型链上定义了get、addDep、cleanupDeps、update、run、evaluate、depend、teardown 方法,进行新增依赖、清除、更新视图等操作。
    每个Vue组件都有一个对应的watcher,这个watcher将会在组件render的时候收集组件所依赖的数据,并在依赖有更新的时候,触发组件重新渲染
    2.proxy代理
    通过 vm 对象(即this)来代理 data 对象中所有属性的操作,更方便的操作 data 中的数据
    通过 Object.defineProperty()给 vm 添加与 data 对象的属性对应的属性描述符,所有添加的属性都包含 getter/setter,getter/setter 内部去操作 data 中对应的属性数据
    3、keep-alive的 缓存原理
    keep-alive是 vue 内置组件,在源码定义中,也具有自己的组件选项如 data 、 method 、 computed 、 props 等,具有抽象组件标识 abstract,通常会与动态组件一同使用,包裹动态组件时,会缓存不活动的组件实例,将它们停用,而不是销毁它们;缓存的组件会触发 activated 或 deactivated 生命周期钩子;会缓存组件实例的 vnode 对象 ,和真实 dom 节点,所以会有 max 属性设置;不会在函数式组件中正常工作,因为它们没有缓存实例
    4、VNode的diff与更新
    销毁一个DOM节点并创建一个新的再插入是消耗非常大的,无论是DOM对象本身的复杂性还是操作引起的重绘重排,所以虚拟DOM的目标是尽可能复用现有DOM进行更新

    三、4000~6200行的代码解读:

      var activeInstance = null;
      var isUpdatingChildComponent = false;
      //设置当前的Vue实例 其返还结果为恢复上一个Vue实例,类似栈的思想来进行树状结构的构建
      function setActiveInstance(vm) {
        var prevActiveInstance = activeInstance;
        activeInstance = vm;
        return function () {
          activeInstance = prevActiveInstance;
        }
      }
      //方法主要用来初始化生命周期相关的属性,以及为parent,child属性赋值
      function initLifecycle (vm) {
        // 定义 options,它是 vm.$options 的引用,后面的代码使用的都是 options 常量
        var options = vm.$options;
        // 查找第一个非抽象父级
        // 定义 parent,它引用当前实例的父实例
        var parent = options.parent;
        // 如果当前实例有父组件,且当前实例不是抽象的
        if (parent && !options.abstract) {
          //循环查找第一个非抽象的父组件
          while (parent.$options.abstract && parent.$parent) {
            parent = parent.$parent;
          }
          // 经过上面的 while 循环后,parent 应该是一个非抽象的组件,将它作为当前实例的父级,
          // 所以将当前实例 vm 添加到父级的 $children 属性里
          parent.$children.push(vm);
        }
        // 设置当前实例的 $parent 属性,指向父级
        vm.$parent = parent;
        // 设置 $root 属性,有父级就是用父级的 $root,否则 $root 指向自身
        vm.$root = parent ? parent.$root : vm;
        // 设置当前实例的 $children 属性为空数组
        vm.$children = [];
        // 设置当前实例的 $ref 属性为空对象
        vm.$refs = {};
        // 设置当前实例的 _watcher 属性为null
        vm._watcher = null;
        vm._inactive = null;
        vm._directInactive = false;
        vm._isMounted = false;
        vm._isDestroyed = false;
        vm._isBeingDestroyed = false;
      }
       //混入生命周期相关属性和方法
      function lifecycleMixin (Vue) {
        Vue.prototype._update = function (vnode, hydrating) {
          // 存储数据做以后update用
          var vm = this;
          var prevEl = vm.$el;
          var prevVnode = vm._vnode;
          var restoreActiveInstance = setActiveInstance(vm);
          vm._vnode = vnode;
          //第一次渲染会调用vm.__patch__方法
          if (!prevVnode) {
            // 初始渲染
            vm.$el = vm.__patch__(vm.$el, vnode, hydrating, false /* removeOnly */);
          } else {
            // 更新
            vm.$el = vm.__patch__(prevVnode, vnode);
          }
          restoreActiveInstance();
          // 更新参考
          if (prevEl) {
            prevEl.__vue__ = null;
          }
          if (vm.$el) {
            vm.$el.__vue__ = vm;
          }
          // 如果parent是HOC,则也更新其$el
          if (vm.$vnode && vm.$parent && vm.$vnode === vm.$parent._vnode) {
            vm.$parent.$el = vm.$el;
          }
          // 调度程序调用更新的挂钩,以确保在父级的更新挂钩中更新子级
        };
        // 强制刷新的作用
        //迫使Vue实例重新渲染,注意它仅仅影响实例本身喝插入插槽内容的子组件,而不是所有的子组件
        Vue.prototype.$forceUpdate = function () {
          var vm = this;
          if (vm._watcher) {
            vm._watcher.update();
          }
        };
        // $destroy是组件内部销毁自己。
        // 清理它与其它实例的连接,解绑它的全部指令及事件监听器,
        // 断掉虚拟dom和真实dom之间的联系,并没有真正地回收这个vue实例
        Vue.prototype.$destroy = function () {
          var vm = this;
          if (vm._isBeingDestroyed) {
            return
          }
          //回调触发beforeDestroy钩子函数
          callHook(vm, 'beforeDestroy');
          vm._isBeingDestroyed = true;
          //从父对象中删除vm
          var parent = vm.$parent;
          if (parent && !parent._isBeingDestroyed && !vm.$options.abstract) {
            remove(parent.$children, vm);
          }
          //拆卸观察者,调用teardown方法移出watcher
          if (vm._watcher) {
            vm._watcher.teardown();
          }
          var i = vm._watchers.length;
          while (i--) {
            vm._watchers[i].teardown();
          }
          // 从数据中删除引用对象
          // 可能没有观察者
          if (vm._data.__ob__) {
            vm._data.__ob__.vmCount--;
          }
          // call the last hook...
          vm._isDestroyed = true;
          //在当前渲染树上调用销毁挂钩
          vm.__patch__(vm._vnode, null);
          //回调触发destroyed钩子函数
          callHook(vm, 'destroyed');
          //关闭所有实例的侦听器
          vm.$off();
          if (vm.$el) {
            vm.$el.__vue__ = null;
          }
          if (vm.$vnode) {
            vm.$vnode.parent = null;
          }
        };
      }
      // $mount函数会调用mountComponent方法
      function mountComponent (
        vm,
        el,//当前挂载的元素
        hydrating //和服务端渲染相关
      ) {
        vm.$el = el;
         // 如果没有!vm.$options.render方法,就创建一个空的VNODE,
        if (!vm.$options.render) {
          vm.$options.render = createEmptyVNode;
          {
            if ((vm.$options.template && vm.$options.template.charAt(0) !== '#') ||
              vm.$options.el || el) {
              warn(
                'You are using the runtime-only build of Vue where the template ' +
                'compiler is not available. Either pre-compile the templates into ' +
                'render functions, or use the compiler-included build.',
                vm
              );
            } else {
              warn(
                'Failed to mount component: template or render function not defined.',
                vm
              );
            }
          }
        }
        //回调触发beforeMount钩子函数
        callHook(vm, 'beforeMount');
    
        var updateComponent;
        /* istanbul ignore if */
        // 如果config中配置了performance(记录性能)
        if (config.performance && mark) {
          updateComponent = function () {
            var name = vm._name;
            var id = vm._uid;
            var startTag = "vue-perf-start:" + id;
            var endTag = "vue-perf-end:" + id;
            //开始标记
            mark(startTag);
            var vnode = vm._render();
             //开始结束
            mark(endTag);
            // measure方法是在执行了Performance的measure方法后,把开始结束两个mark值删除
            measure(("vue " + name + " render"), startTag, endTag);
    
            mark(startTag);
            vm._update(vnode, hydrating);
            mark(endTag);
            measure(("vue " + name + " patch"), startTag, endTag);
          };
        } else {
          updateComponent = function () {
            vm._update(vm._render(), hydrating);
          };
        }
    
        //将其设置为vm._watcher在watcher的构造函数中
        //因为观察者的初始补丁可能会调用$forceUpdate(例如,在child内部组件的挂载钩子),它依赖于已定义的vm.\u监视程序
        new Watcher(vm, updateComponent, noop, {
          // vm._isMounted 为 true,表示这个实例已经挂载了,执行 beforeUpdate 钩子函数
          before: function before () {
            if (vm._isMounted && !vm._isDestroyed) {
              callHook(vm, 'beforeUpdate');
            }
          }
        }, true);
        hydrating = false;
    
        //手动装入实例,调用自行装入
        //在插入的钩子中为渲染创建的子组件调用mounted
        if (vm.$vnode == null) {
          vm._isMounted = true;
          callHook(vm, 'mounted');
        }
        return vm
      }
        //对占位符 vm.$vnode 的更新、slot的更新,listeners 的更新,props 的更新等
      function updateChildComponent (
        vm,
        propsData,
        listeners,
        parentVnode,
        renderChildren
      ) {
        {
          isUpdatingChildComponent = true;
        }
    
        // 确定组件是否具有插槽子级
        var newScopedSlots = parentVnode.data.scopedSlots;
        var oldScopedSlots = vm.$scopedSlots;
        var hasDynamicScopedSlot = !!(
          (newScopedSlots && !newScopedSlots.$stable) ||
          (oldScopedSlots !== emptyObject && !oldScopedSlots.$stable) ||
          (newScopedSlots && vm.$scopedSlots.$key !== newScopedSlots.$key) ||
          (!newScopedSlots && vm.$scopedSlots.$key)
        );
    
        // 来自父级的任何静态插槽子级在父级的更新。动态作用域插槽也可能已更改。
        // 在这种情况下,需要强制更新以确保正确性
        var needsForceUpdate = !!(
          renderChildren ||               // 有一个新的静态插槽
          vm.$options._renderChildren ||  // 有一个旧的静态插槽
          hasDynamicScopedSlot
        );
    
        vm.$options._parentVnode = parentVnode;
        // 更新vm的占位符节点而不重新渲染
        vm.$vnode = parentVnode;
        // 更新子节点的父节点
        if (vm._vnode) { 
          vm._vnode.parent = parentVnode;
        }
        vm.$options._renderChildren = renderChildren;
    
        // 更新$attrs和$listeners散列它们也是被动的,因此如果子对象在渲染期间使用它们,
        // 它们可能会触发子对象更新
        vm.$attrs = parentVnode.data.attrs || emptyObject;
        vm.$listeners = listeners || emptyObject;
    
        // 更新props
        if (propsData && vm.$options.props) {
          //禁止掉根组件 props的依赖收集
          toggleObserving(false);
          var props = vm._props;
          var propKeys = vm.$options._propKeys || [];
          for (var i = 0; i < propKeys.length; i++) {
            var key = propKeys[i];
            var propOptions = vm.$options.props;
            //校验props
            props[key] = validateProp(key, propOptions, propsData, vm);
          }
          toggleObserving(true);
          // 保留一份原始的propsData
          vm.$options.propsData = propsData;
        }
    
        // 更新listeners
        listeners = listeners || emptyObject;
        var oldListeners = vm.$options._parentListeners;
        vm.$options._parentListeners = listeners;
        updateComponentListeners(vm, listeners, oldListeners);
    
        // 解决插槽和强制更新
        if (needsForceUpdate) {
          vm.$slots = resolveSlots(renderChildren, parentVnode.context);
          vm.$forceUpdate();
        }
    
        {
          isUpdatingChildComponent = false;
        }
      }
      //判断是否直接激活
      function isInInactiveTree (vm) {
        while (vm && (vm = vm.$parent)) {
          if (vm._inactive) { return true }
        }
        return false
      }
      // 激活子组件
      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;
          // 循环激活 vm.$children
          for (var i = 0; i < vm.$children.length; i++) {
            activateChildComponent(vm.$children[i]);
          }
          // 调用 callHook(vm, 'activated')
          callHook(vm, 'activated');
        }
      }
      // 不激活组件
      function deactivateChildComponent (vm, direct) {
        if (direct) {
          vm._directInactive = true;
          if (isInInactiveTree(vm)) {
            return
          }
        }
        // 判断是否是直接不激活
        if (!vm._inactive) {
          vm._inactive = true;
          // 循环不激活 vm.$children
          for (var i = 0; i < vm.$children.length; i++) {
            deactivateChildComponent(vm.$children[i]);
          }
          // 调用 callHook(vm, "deactivated")
          callHook(vm, 'deactivated');
        }
      }
      // 先入栈操作,拿到 options.hook,处理错误问题,vm.$emit('hook:' + hook),出栈操作
      function callHook (vm, hook) {
        // 在调用生命周期钩子时禁用dep收集
        pushTarget();
        var handlers = vm.$options[hook];
        var info = hook + " hook";
        if (handlers) {
          for (var i = 0, j = handlers.length; i < j; i++) {
            invokeWithErrorHandling(handlers[i], vm, null, vm, info);
          }
        }
        if (vm._hasHookEvent) {
          vm.$emit('hook:' + hook);
        }
        popTarget();
      }
    
      /*  */
    
      var MAX_UPDATE_COUNT = 100;
    
      var queue = [];
      var activatedChildren = [];
      var has = {};
      var circular = {};
      var waiting = false;
      var flushing = false;
      var index = 0;
    
      /**
       * 重置scheduler的状态.
       */
      function resetSchedulerState () {
        index = queue.length = activatedChildren.length = 0;
        has = {};
        {
          circular = {};
        }
        waiting = flushing = false;
      }
    
      // 需要在附加事件监听器时保存时间戳。但是,调用performance.now()会带来性能开销,
      // 尤其是当页面有数千个事件监听器时。相反,我们在调度程序每次刷新时获取一个时间戳,并将其用于刷新期间附加的所有事件监听器。
      var currentFlushTimestamp = 0;
    
      // 异步边缘情况修复需要存储事件侦听器的附加时间戳
      var getNow = Date.now;
    
      // 确定浏览器正在使用的事件时间戳。恼人的是,时间戳可以是高分辨率(相对于页面加载),
      // 也可以是低分辨率(相对于UNIX epoch),所以为了比较时间,
      // 我们必须在保存刷新时间戳时使用相同的时间戳类型。所有IE版本都使用低分辨率的事件时间戳,并且时钟实现存在问题
      if (inBrowser && !isIE) {
        var performance = window.performance;
        if (
          performance &&
          typeof performance.now === 'function' &&
          getNow() > document.createEvent('Event').timeStamp
        ) {
          // 如果事件时间戳(尽管在Date.now()之后计算)小于它,则意味着事件使用的是高分辨率时间戳,
          // 我们也需要为事件侦听器的时间戳使用高分辨率版本。
          getNow = function () { return performance.now(); };
        }
      }
    
      /**
       * 清空两个队列并运行监视器。
       * 据变化最终会把flushSchedulerQueue传入到nextTick中执行
       * 遍历执行watcher.run()方法,watcher.run()方法最终会完成视图更新
       */
      function flushSchedulerQueue () {
        currentFlushTimestamp = getNow();
        flushing = true;
        var watcher, id;
    
        //在刷新之前对队列进行排序。为了确保:
        // 1。组件从父组件更新到子组件。(因为父母总是
        //在child之前创建
        // 2。组件的用户监视器在它的呈现监视器之前运行(因为
        //用户观察者在渲染观察者之前创建)
        // 3。如果一个组件在父组件的监视程序运行期间被销毁,
        //它的观察者可以被跳过。
        queue.sort(function (a, b) { return a.id - b.id; });
    
        //不要缓存长度,因为可能会有更多的监视器被推送
        //我们运行现有的观察者
        for (index = 0; index < queue.length; index++) {
          watcher = queue[index];
          if (watcher.before) {
            watcher.before();
          }
          id = watcher.id;
          has[id] = null;
          watcher.run();
          // 在开发构建中,检查并停止循环更新。
          if (has[id] != null) {
            circular[id] = (circular[id] || 0) + 1;
            if (circular[id] > MAX_UPDATE_COUNT) {
              warn(
                'You may have an infinite update loop ' + (
                  watcher.user
                    ? ("in watcher with expression \"" + (watcher.expression) + "\"")
                    : "in a component render function."
                ),
                watcher.vm
              );
              break
            }
          }
        }
    
        // 在重置状态之前保留帖子队列的副本
        var activatedQueue = activatedChildren.slice();
        var updatedQueue = queue.slice();
    
        resetSchedulerState();
    
        // 执行actived钩子函数
        callActivatedHooks(activatedQueue);
        //执行updated钩子函数
        callUpdatedHooks(updatedQueue);
    
        // devtool hook
        /* istanbul ignore if */
        if (devtools && config.devtools) {
          devtools.emit('flush');
        }
      }
      // 按索引递减的顺序执行_watcher关联实例的updated钩子
      function callUpdatedHooks (queue) {
        var i = queue.length;
        while (i--) {
          var watcher = queue[i];
          var vm = watcher.vm;
          if (vm._watcher === watcher && vm._isMounted && !vm._isDestroyed) {
            callHook(vm, 'updated');
          }
        }
      }
    
      /**
       *队列一个在补丁期间激活的保持活动的组件。
       *队列将在整个树被修补后进行处理。
       */
      function queueActivatedComponent (vm) {
        //将_inactive设置为false,这样渲染函数就可以
        //依赖于检查它是否在一个非活动的树(例如路由器视图)
        vm._inactive = false;
        activatedChildren.push(vm);
      }
    
      function callActivatedHooks (queue) {
        for (var i = 0; i < queue.length; i++) {
          queue[i]._inactive = true;
          activateChildComponent(queue[i], true /* true */);
        }
      }
    
      /**
       * 将一个观察者推入观察者队列。具有重复id的作业将被跳过,除非它是在队列刷新时被推入。
       */
      function queueWatcher (watcher) {
        var id = watcher.id;
        if (has[id] == null) {
          has[id] = true;
          if (!flushing) {
            queue.push(watcher);
          } else {
            // 如果已经刷新,则根据其id拼接监视程序
            // 如果已经超过了它的id,它将立即运行下一个
            var i = queue.length - 1;
            while (i > index && queue[i].id > watcher.id) {
              i--;
            }
            queue.splice(i + 1, 0, watcher);
          }
          if (!waiting) {
            waiting = true;
    
            if (!config.async) {
              // 头尾插入代码来获取耗费时间
              flushSchedulerQueue();
              return
            }
            nextTick(flushSchedulerQueue);
          }
        }
      }
    
      /*  */
    
    
    
      var uid$2 = 0;
    
      /**
       * 观察者解析表达式,收集依赖项,并在表达式值改变时触发回调。这用于$watch() api和指令
       * Watcher 用于初始化数据的watcher的实列,他的原型上有个update用于派发更新
       */
      var Watcher = function Watcher (
        vm,
        expOrFn,
        cb,
        options,
        isRenderWatcher
      ) {
        this.vm = vm;
        if (isRenderWatcher) {
          vm._watcher = this;
        }
        vm._watchers.push(this);
        // options
        if (options) {
          this.deep = !!options.deep;
          this.user = !!options.user;
          this.lazy = !!options.lazy;
          this.sync = !!options.sync;
          this.before = options.before;
        } else {
          this.deep = this.user = this.lazy = this.sync = false;
        }
        this.cb = cb;
        this.id = ++uid$2; // uid for batching
        this.active = true;
        this.dirty = this.lazy; // 用于懒惰的观察者
        this.deps = [];
        this.newDeps = [];
        this.depIds = new _Set();
        this.newDepIds = new _Set();
        this.expression = expOrFn.toString();
        // getter的解析表达式
        if (typeof expOrFn === 'function') {
          this.getter = expOrFn;
        } else {
          this.getter = parsePath(expOrFn);
          if (!this.getter) {
            this.getter = noop;
            warn(
              "Failed watching path: \"" + expOrFn + "\" " +
              'Watcher only accepts simple dot-delimited paths. ' +
              'For full control, use a function instead.',
              vm
            );
          }
        }
        this.value = this.lazy
          ? undefined
          : this.get();
      };
    
      /**
       * 评估getter,并重新收集依赖项。
       */
      Watcher.prototype.get = function get () {
        //将当前用户watch保存到Dep.target中
        pushTarget(this);
        var value;
        var vm = this.vm;
        try {
          //执行用户wathcer的getter()方法,此方法会将当前用户watcher作为订阅者订阅起来
          value = this.getter.call(vm, vm);
        } catch (e) {
          if (this.user) {
            handleError(e, vm, ("getter for watcher \"" + (this.expression) + "\""));
          } else {
            throw e
          }
        } finally {
          //"touch"每一个属性,所以他们都被跟踪
          //深度监视的依赖项
          if (this.deep) {
            traverse(value);
          }
          //恢复之前的watcher
          popTarget();
          this.cleanupDeps();
        }
        return value
      };
    
      /**
       *给这个指令添加一个依赖项.
       添加依赖(重点)
       */
      Watcher.prototype.addDep = function addDep (dep) {
        var id = dep.id;
        if (!this.newDepIds.has(id)) {
          //将依赖的每个dep,添加到 watcher 的 deps集合中,完成数据的收集
          this.newDepIds.add(id);
          this.newDeps.push(dep);
          if (!this.depIds.has(id)) {
            dep.addSub(this);
          }
        }
      };
    
      /**
       * 清理依赖项集合.
       */
      Watcher.prototype.cleanupDeps = function cleanupDeps () {
        var i = this.deps.length;
        while (i--) {
          var dep = this.deps[i];
          if (!this.newDepIds.has(dep.id)) {
            dep.removeSub(this);
          }
        }
        var tmp = this.depIds;
        this.depIds = this.newDepIds;
        this.newDepIds = tmp;
        this.newDepIds.clear();
        tmp = this.deps;
        // 把 this.deps = this.newDeps,缓存到 deps 里,然后清空newDeps,来做下一次的收集
        this.deps = this.newDeps;
        this.newDeps = tmp;
        this.newDeps.length = 0;
      };
    
      /**
       * 更新数据的方法,在派发更新的时候会用到。 computed 更新数据的时候,用 dep 的 notify 方法进 
       * 行更新数据,更新数据主要调用的是 run 方法
       */
      Watcher.prototype.update = function update () {
        /* istanbul ignore else */
        if (this.lazy) {
          this.dirty = true;
        } else if (this.sync) {
          this.run();
        } else {
          queueWatcher(this);
        }
      };
    
      /**
       * 在这个阶段主要是运行get方法,拿到数据
       */
      Watcher.prototype.run = function run () {
        if (this.active) {
          var value = this.get();
          if (
            value !== this.value ||
            // 深度观察者和对象/数组的观察者应该发射,当值相同时,因为值可能有突变。
            isObject(value) ||
            this.deep
          ) {
            //设置新的值
            var oldValue = this.value;
            this.value = value;
            if (this.user) {
              var info = "callback for watcher \"" + (this.expression) + "\"";
              invokeWithErrorHandling(this.cb, this.vm, [value, oldValue], this.vm, info);
            } else {
              this.cb.call(this.vm, value, oldValue);
            }
          }
        }
      };
    
      // 用来重新计算,更新缓存值,并重置 dirty 为false,表示缓存已更新
      Watcher.prototype.evaluate = function evaluate () {
        this.value = this.get();
         // 执行完更新函数之后,立即重置标志
        this.dirty = false;
      };
    
      /**
       * 深度收集依赖,computed 可以收集 computed 数据就是依靠这个方法
       */
      Watcher.prototype.depend = function depend () {
        var i = this.deps.length;
        while (i--) {
          //注意这里的 depend 方法是 Dep 原型上的方法,不是Watcher 的法
          this.deps[i].depend();
        }
      };
    
      /**
       * 从所有依赖项的订阅者列表中删除self
       */
      Watcher.prototype.teardown = function teardown () {
        if (this.active) {
          // 从vm的监视列表中移除self这是一个有点昂贵的操作,所以我们跳过它,如果vm正在被销毁。
          if (!this.vm._isBeingDestroyed) {
            remove(this.vm._watchers, this);
          }
          var i = this.deps.length;
          while (i--) {
            this.deps[i].removeSub(this);
          }
          this.active = false;
        }
      };
    
      //***************************************代理proxy*************************** */
      /* 初始化 */
    
      var sharedPropertyDefinition = {
        enumerable: true,
        configurable: true,
        get: noop,
        set: noop
      };
      // 定义代理函数,target:当前对象,sourceKey:代理对象的名称,key:要访问的属性
      function proxy (target, sourceKey, key) {
        sharedPropertyDefinition.get = function proxyGetter () {
          //访问vm[message],就返回vm[data][message]
          return this[sourceKey][key]
        };
        //设值vm[message],就变成设值vm[data][message]
        sharedPropertyDefinition.set = function proxySetter (val) {
          this[sourceKey][key] = val;
        };
        //通过defineProperty对vm[message]进行拦截,并将拦截的set和get方法通过sharedPropertyDefinition传进去
        Object.defineProperty(target, key, sharedPropertyDefinition);
      }
      //初始化实例状态
      function initState (vm) {
        vm._watchers = [];
        var opts = vm.$options;
        //初始化props和methods
        if (opts.props) { initProps(vm, opts.props); }
        if (opts.methods) { initMethods(vm, opts.methods); }
        if (opts.data) {
          //把data属性注入到Vue实例上
          initData(vm);
        } else {
          // 调用observe(data)将data对象转化成响应式的对象
          observe(vm._data = {}, true /* asRootData */);
        }
        //初始化computed
        if (opts.computed) { initComputed(vm, opts.computed); }
        if (opts.watch && opts.watch !== nativeWatch) {
          //初始化watch
          initWatch(vm, opts.watch);
        }
      }
    
      function initProps (vm, propsOptions) {
        var propsData = vm.$options.propsData || {};
        var props = vm._props = {};
        // 缓存props的key,以便将来的道具更新可以使用Array进行迭代,而不是使用动态对象键枚举。
        var keys = vm.$options._propKeys = [];
        var isRoot = !vm.$parent;
        // 根实例道具需要被转换
        if (!isRoot) {
          toggleObserving(false);
        }
        var loop = function ( key ) {
          keys.push(key);
          //用来处理校验规范,设置默认值,取值传递过来得参数值等操作
          var value = validateProp(key, propsOptions, propsData, vm);
          /* istanbul ignore else */
          {
            var hyphenatedKey = hyphenate(key);
            if (isReservedAttribute(hyphenatedKey) ||
                config.isReservedAttr(hyphenatedKey)) {
              warn(
                ("\"" + hyphenatedKey + "\" is a reserved attribute and cannot be used as component prop."),
                vm
              );
            }
            // 来处理props中参数得响应式
            defineReactive$$1(props, key, value, function () {
              if (!isRoot && !isUpdatingChildComponent) {
                warn(
                  "Avoid mutating a prop directly since the value will be " +
                  "overwritten whenever the parent component re-renders. " +
                  "Instead, use a data or computed property based on the prop's " +
                  "value. Prop being mutated: \"" + key + "\"",
                  vm
                );
              }
            });
          }
          // 在Vue.extend()期间,静态道具已经被代理到组件的原型上了,只需要在这里实例化时定义的代理道具。
          if (!(key in vm)) {
            proxy(vm, "_props", key);
          }
        };
    
        for (var key in propsOptions) loop( key );
        toggleObserving(true);
      }
      // 运行 observe 函数深度遍历数据中的每一个属性,进行数据劫持
      function initData (vm) {
        var data = vm.$options.data;
        data = vm._data = typeof data === 'function'
          ? getData(data, vm)
          : data || {};
        if (!isPlainObject(data)) {
          data = {};
          warn(
            'data functions should return an object:\n' +
            'https://vuejs.org/v2/guide/components.html#data-Must-Be-a-Function',
            vm
          );
        }
        // proxy data on instance
        var keys = Object.keys(data);
        var props = vm.$options.props;
        var methods = vm.$options.methods;
        var i = keys.length;
        while (i--) {
          var key = keys[i];
          {
            if (methods && hasOwn(methods, key)) {
              warn(
                ("Method \"" + key + "\" has already been defined as a data property."),
                vm
              );
            }
          }
          if (props && hasOwn(props, key)) {
            warn(
              "The data property \"" + key + "\" is already declared as a prop. " +
              "Use prop default value instead.",
              vm
            );
          } else if (!isReserved(key)) {
            proxy(vm, "_data", key);
          }
        }
        // observe data
        observe(data, true /* asRootData */);
      }
      
      function getData (data, vm) {
        // 在调用数据获取器时禁用dep收集
        pushTarget();
        try {
          return data.call(vm, vm)
        } catch (e) {
          handleError(e, vm, "data()");
          return {}
        } finally {
          popTarget();
        }
      }
    
      var computedWatcherOptions = { lazy: true };
      // 始化computed的时候(initComputed),会监测数据是否已经存在data或props上,如果存在则抛出警告,
      // 否则调用defineComputed函数,监听数据,为组件中的属性绑定getter及setter。如果computed中属性的值是一个函数,
      // 则默认为属性的getter函数。此外属性的值还可以是一个对象,他只有三个有效字段set、get和cache,
      // 分别表示属性的setter、getter和是否启用缓存,其中get是必须的,cache默认为true
    
      function initComputed (vm, computed) {
        var watchers = vm._computedWatchers = Object.create(null);
        // 计算属性只是SSR时期的getter
        var isSSR = isServerRendering();
    
        for (var key in computed) {
          var userDef = computed[key];
          var getter = typeof userDef === 'function' ? userDef : userDef.get;
          if (getter == null) {
            warn(
              ("Getter is missing for computed property \"" + key + "\"."),
              vm
            );
          }
    
          if (!isSSR) {
            // 为computed属性创建内部监视器。
            watchers[key] = new Watcher(
              vm,
              getter || noop,
              noop,
              computedWatcherOptions
            );
          }
    
          // 如果定义的计算属性不在组件实例上,对属性进行数据劫持
          if (!(key in vm)) {
            defineComputed(vm, key, userDef);
          } else {
            // 如果定义的计算属性在data和props有,抛出警告
            if (key in vm.$data) {
              warn(("The computed property \"" + key + "\" is already defined in data."), vm);
            } else if (vm.$options.props && key in vm.$options.props) {
              warn(("The computed property \"" + key + "\" is already defined as a prop."), vm);
            } else if (vm.$options.methods && key in vm.$options.methods) {
              warn(("The computed property \"" + key + "\" is already defined as a method."), vm);
            }
          }
        }
      }
    // 监听数据,为组件中的属性绑定getter及sette
      function defineComputed (
        target,
        key,
        userDef
      ) {
        var shouldCache = !isServerRendering();
        if (typeof userDef === 'function') {
          sharedPropertyDefinition.get = shouldCache
            ? createComputedGetter(key)
            : createGetterInvoker(userDef);
          sharedPropertyDefinition.set = noop;
        } else {
          sharedPropertyDefinition.get = userDef.get
            ? shouldCache && userDef.cache !== false
              ? createComputedGetter(key)
              : createGetterInvoker(userDef.get)
            : noop;
          sharedPropertyDefinition.set = userDef.set || noop;
        }
        if (sharedPropertyDefinition.set === noop) {
          sharedPropertyDefinition.set = function () {
            warn(
              ("Computed property \"" + key + "\" was assigned to but it has no setter."),
              this
            );
          };
        }
        Object.defineProperty(target, key, sharedPropertyDefinition);
      }
    // 创建计算属性的getter函数computedGetter
      function createComputedGetter (key) {
        return function computedGetter () {
          var watcher = this._computedWatchers && this._computedWatchers[key];
          if (watcher) {
            if (watcher.dirty) {
              //watcher.evaluate中更新watcher的值,并把watcher.dirty设置为false
                    //这样等下次依赖更新的时候才会把watcher.dirty设置为true,然后进行取值的时候才会再次运行这个函数
              watcher.evaluate();
            }
            //依赖追踪
            if (Dep.target) {
              watcher.depend();
            }
            return watcher.value
          }
        }
      }
    
      function createGetterInvoker(fn) {
        // 没有缓存则直接执行computed的getter函数
        return function computedGetter () {
          return fn.call(this, this)
        }
      }
    // 绑定了所有method的this为vm
      function initMethods (vm, methods) {
        var props = vm.$options.props;
        for (var key in methods) {
          {
            if (typeof methods[key] !== 'function') {
              warn(
                "Method \"" + key + "\" has type \"" + (typeof methods[key]) + "\" in the component definition. " +
                "Did you reference the function correctly?",
                vm
              );
            }
            if (props && hasOwn(props, key)) {
              warn(
                ("Method \"" + key + "\" has already been defined as a prop."),
                vm
              );
            }
            if ((key in vm) && isReserved(key)) {
              warn(
                "Method \"" + key + "\" conflicts with an existing Vue instance method. " +
                "Avoid defining component methods that start with _ or $."
              );
            }
          }
          vm[key] = typeof methods[key] !== 'function' ? noop : bind(methods[key], vm);
        }
      }
      // vm.$watch函数会直接使用Watcher构建观察者对象。watch中属性的值作为watcher.cb存在,在观察者update的时候,在watcher.run函数中执行
      function initWatch (vm, watch) {
        //遍历watch,为每一个属性创建侦听器
        for (var key in watch) {
          var handler = watch[key];
          //如果属性值是一个数组,则遍历数组,为属性创建多个侦听器
            //createWatcher函数中封装了vm.$watch,会在vm.$watch中创建侦听器
          if (Array.isArray(handler)) {
            for (var i = 0; i < handler.length; i++) {
              createWatcher(vm, key, handler[i]);
            }
          } else {
            //为属性创建侦听器
            createWatcher(vm, key, handler);
          }
        }
      }
      function createWatcher (
        vm,
        expOrFn,
        handler,
        options
      ) {
        //如果属性值是一个对象,则取对象的handler属性作为回调
        if (isPlainObject(handler)) {
          options = handler;
          handler = handler.handler;
        }
        //如果属性值是一个字符串,则从组件实例上寻找
        if (typeof handler === 'string') {
          handler = vm[handler];
        }
        //为属性创建侦听器
        return vm.$watch(expOrFn, handler, options)
      }
    
      function stateMixin (Vue) {
        // 在使用object . defineproperty时,flow在直接声明定义对象方面有一些问题,所以我们必须在这里过程地构建对象。
        // 定义了dataDef和propsDef,主要是用来设置响应式d a t a 和 data和data和props的get和set方法
        var dataDef = {};
        dataDef.get = function () { return this._data };
        var propsDef = {};
        propsDef.get = function () { return this._props };
        {
          dataDef.set = function () {
            warn(
              'Avoid replacing instance root $data. ' +
              'Use nested data properties instead.',
              this
            );
          };
          propsDef.set = function () {
            warn("$props is readonly.", this);
          };
        }
        Object.defineProperty(Vue.prototype, '$data', dataDef);
        Object.defineProperty(Vue.prototype, '$props', propsDef);
    
        Vue.prototype.$set = set;
        Vue.prototype.$delete = del;
    
        Vue.prototype.$watch = function (
          expOrFn,
          cb,
          options
        ) {
          var vm = this;
          if (isPlainObject(cb)) {
            return createWatcher(vm, expOrFn, cb, options)
          }
          options = options || {};
          options.user = true;
          options.user = true;
          //为需要观察的 expOrFn 添加watcher ,expOrFn的值有改变时执行cb,
          //在watcher的实例化的过程中会对expOrFn进行解析,并为expOrFn涉及到的data数据下的def添加该watcher
          var watcher = new Watcher(vm, expOrFn, cb, options);
          if (options.immediate) {
            var info = "callback for immediate watcher \"" + (watcher.expression) + "\"";
            pushTarget();
            invokeWithErrorHandling(cb, vm, [watcher.value], vm, info);
            popTarget();
          }
           //取消观察函数
          return function unwatchFn () {
            watcher.teardown();
          }
        };
      }
    
      /*  */
    
      var uid$3 = 0;
    
      function initMixin (Vue) {
        Vue.prototype._init = function (options) {
          var vm = this;
          // a uid
          vm._uid = uid$3++;
    
          var startTag, endTag;
          /* istanbul ignore if */
          if (config.performance && mark) {
            startTag = "vue-perf-start:" + (vm._uid);
            endTag = "vue-perf-end:" + (vm._uid);
            mark(startTag);
          }
    
          // 如果是Vue的实例,则不需要被observe
          vm._isVue = true;
          // options参数的处理
          if (options && options._isComponent) {
            // optimize internal component instantiation
            // since dynamic options merging is pretty slow, and none of the
            // internal component options needs special treatment.
            initInternalComponent(vm, options);
          } else {
            vm.$options = mergeOptions(
              resolveConstructorOptions(vm.constructor),
              options || {},
              vm
            );
          }
          /* istanbul ignore else */
          {
            initProxy(vm);
          }
          // expose real self
          vm._self = vm;
          // vm的生命周期相关变量初始化
          initLifecycle(vm);
          // vm的事件监听初始化
          initEvents(vm);
          // vm的编译render初始化
          initRender(vm);
          // vm的beforeCreate生命钩子的回调
          callHook(vm, 'beforeCreate');
          // vm在data/props初始化之前要进行绑定
          initInjections(vm); // resolve injections before data/props
          // vm的sate状态初始化
          initState(vm);
          // vm在data/props之后要进行提供
          initProvide(vm); // resolve provide after data/props
          // vm的created生命钩子的回调
          callHook(vm, 'created');
    
          /* istanbul ignore if */
          if (config.performance && mark) {
            vm._name = formatComponentName(vm, false);
            mark(endTag);
            measure(("vue " + (vm._name) + " init"), startTag, endTag);
          }
    
          if (vm.$options.el) {
            vm.$mount(vm.$options.el);
          }
        };
      }
      // Vue初始化中的选项合并
      // 通过循环递归,找到Component这个类的继承链,然后把所有的配置都进行融合
      function initInternalComponent (vm, options) {
        var opts = vm.$options = Object.create(vm.constructor.options);
        // 该组件实例的vnode对象
        var parentVnode = options._parentVnode;
        opts.parent = options.parent;
        opts._parentVnode = parentVnode;
    
        var vnodeComponentOptions = parentVnode.componentOptions;
        opts.propsData = vnodeComponentOptions.propsData;
        opts._parentListeners = vnodeComponentOptions.listeners;
        opts._renderChildren = vnodeComponentOptions.children;
        opts._componentTag = vnodeComponentOptions.tag;
    
        if (options.render) {
          opts.render = options.render;
          opts.staticRenderFns = options.staticRenderFns;
        }
      }
    
      function resolveConstructorOptions (Ctor) {
        var options = Ctor.options;
        // 首先需要判断该类是否是Vue的子类
        if (Ctor.super) {
          var superOptions = resolveConstructorOptions(Ctor.super);
          var cachedSuperOptions = Ctor.superOptions;
           // 来判断父类中的options 有没有发生变化
          if (superOptions !== cachedSuperOptions) {
            // 超级选项改变,需要解决新的选项
            Ctor.superOptions = superOptions;
            // 检查是否有任何后期修改/附加的选项
            var modifiedOptions = resolveModifiedOptions(Ctor);
            // update base extend options
            if (modifiedOptions) {
              // 当为Vue混入一些options时,superOptions会发生变化,此时于之前子类中存储的cachedSuperOptions已经不相等,所以下面的操作主要就是更新sub.superOptions
              extend(Ctor.extendOptions, modifiedOptions);
            }
            options = Ctor.options = mergeOptions(superOptions, Ctor.extendOptions);
            if (options.name) {
              options.components[options.name] = Ctor;
            }
          }
        }
        return options
      }
    
      function resolveModifiedOptions (Ctor) {
        var modified;
        var latest = Ctor.options;
        // 执行Vue.extend时封装的"自身"options,这个属性就是方便检查"自身"的options有没有变化
        var sealed = Ctor.sealedOptions;
        // 遍历当前构造器上的options属性,如果在"自身"封装的options里没有,则证明是新添加的。执行if内的语句。
        // 调用dedupe方法,最终返回modified变量(即”自身新添加的options“)
      for (const key in latest) {
        for (var key in latest) {
          if (latest[key] !== sealed[key]) {
            if (!modified) { modified = {}; }
            modified[key] = latest[key];
          }
        }
        return modified
      }
    }
    
      function Vue (options) {
        if (!(this instanceof Vue)
        ) {
          warn('Vue is a constructor and should be called with the `new` keyword');
        }
        this._init(options);
      }
      initMixin(Vue);// 初始化 Mixin
      stateMixin(Vue);// 状态 Mixin
      eventsMixin(Vue);// 事件 Mixin
      lifecycleMixin(Vue);// 生命周期 Mixin
      renderMixin(Vue);// 渲染 Mixin
    
      /*  Vue.use() 的注册本质上就是执行了一个 install 方法 */
      function initUse (Vue) {
        // 在全局api Vue 上定义了 use 方法,接收一个 plugin 参数
        Vue.use = function (plugin) {
          var installedPlugins = (this._installedPlugins || (this._installedPlugins = []));
          if (installedPlugins.indexOf(plugin) > -1) {
            return this
          }
    
          // 用来判断该插件是不是已经注册过,防止重复注册
          var args = toArray(arguments, 1);
          args.unshift(this);
          // 判断 Vue.use() 传入的第一个参数是 Object 还是 Function
          if (typeof plugin.install === 'function') {
            plugin.install.apply(plugin, args);
          } else if (typeof plugin === 'function') {
            plugin.apply(null, args);
          }
          installedPlugins.push(plugin);
          return this
        };
      }
    
      /* 将options和mixin合并 */
    
      function initMixin$1 (Vue) {
        Vue.mixin = function (mixin) {
          this.options = mergeOptions(this.options, mixin);
          return this
        };
      }
    
        /**
         * Vue.extend的原理以及初始化过程
         */
    
      function initExtend (Vue) {
        // 每个实例构造函数(包括Vue)都有一个唯一的cid。这使我们能够为原型继承创建包装子构造函数并缓存它们。
        Vue.cid = 0;
        var cid = 1;
    
    
        Vue.extend = function (extendOptions) {
          // extendOptions就是我我们传入的组件options
          extendOptions = extendOptions || {};
          var Super = this;
          var SuperId = Super.cid;
              // 每次创建完Sub构造函数后,都会把这个函数储存在extendOptions上的_Ctor中
              // 下次如果用再同一个extendOptions创建Sub时
              // 就会直接从_Ctor返回
          var cachedCtors = extendOptions._Ctor || (extendOptions._Ctor = {});
          if (cachedCtors[SuperId]) {
            return cachedCtors[SuperId]
          }
          //校验组件 名
          var name = extendOptions.name || Super.options.name;
          if (name) {
            validateComponentName(name);
          }
          // 创建Sub构造函数
          var Sub = function VueComponent (options) {
            this._init(options);
          };
          // 继承Super,如果使用Vue.extend,这里的Super就是Vue
          Sub.prototype = Object.create(Super.prototype);
          Sub.prototype.constructor = Sub;
          Sub.cid = cid++;
          Sub.options = mergeOptions(
            Super.options,
            extendOptions
          );
           // 将组件的options和Vue的options合并,得到一个完整的options
        // 可以理解为将Vue的一些全局的属性,比如全局注册的组件和mixin,分给了Sub
          Sub['super'] = Super;
    
          // 将props和computed代理到了原型上
          if (Sub.options.props) {
            initProps$1(Sub);
          }
          if (Sub.options.computed) {
            initComputed$1(Sub);
          }
    
           // 继承Vue的global-api
          Sub.extend = Super.extend;
          Sub.mixin = Super.mixin;
          Sub.use = Super.use;
    
         // 继承assets的api,比如注册组件,指令,过滤器
          ASSET_TYPES.forEach(function (type) {
            Sub[type] = Super[type];
          });
          // 在components里添加一个自己
          if (name) {
            Sub.options.components[name] = Sub;
          }
    
          // 这些options保存起来
          Sub.superOptions = Super.options;
          Sub.extendOptions = extendOptions;
          Sub.sealedOptions = extend({}, Sub.options);
    
           // 设置缓存
          cachedCtors[SuperId] = Sub;
          return Sub
        };
      }
      //初始化自定义代理函数
      function initProps$1 (Comp) {
        var props = Comp.options.props;
        for (var key in props) {
          proxy(Comp.prototype, "_props", key);
        }
      }
      //为组件的属性绑定setter和getter属性
      function initComputed$1 (Comp) {
        var computed = Comp.options.computed;
        for (var key in computed) {
          defineComputed(Comp.prototype, key, computed[key]);
        }
      }
    
      /* 创建组件、指令、过滤器 */
    
      function initAssetRegisters (Vue) {
        //遍历 ASSET_TYPES 数组,为Vue 定义相应的方法
        // ASSET_TYPES 包含 directive  component  filter
        ASSET_TYPES.forEach(function (type) {
          Vue[type] = function (
            id,
            definition
          ) {
             // 判断是否有第二个参数,没有的话去之前的option的组件或者指令
            if (!definition) {
              return this.options[type + 's'][id]
            } else {
              //判断是否是一个组件,校验组件名字
              if (type === 'component') {
                validateComponentName(id);
              }
               // 判断传入的是不是一个原始对象
              if (type === 'component' && isPlainObject(definition)) {
                definition.name = definition.name || id;
                // 把组件配置转换为组件的构造函数
                definition = this.options._base.extend(definition);
              }
              if (type === 'directive' && typeof definition === 'function') {
                definition = { bind: definition, update: definition };
              }
               // 全局注册,存储资源赋值
              this.options[type + 's'][id] = definition;
              return definition
            }
          };
        });
      }
    //获取组件的名字
      function getComponentName (opts) {
        return opts && (opts.Ctor.options.name || opts.tag)
      }
      // 检测name是否匹配
      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) {
        //获取keepAliveInstance的缓存
        var cache = keepAliveInstance.cache;
        var keys = keepAliveInstance.keys;
        var _vnode = keepAliveInstance._vnode;
        for (var key in cache) {
          var entry = cache[key];
          if (entry) {
            var name = entry.name;
            // name不符合filter条件的,同时不是目前渲染的vnode时,销毁vnode对应的组件实例(Vue实例),并从cache中移除
            if (name && !filter(name)) {
              //pruneCache 函数的核心就是去调用pruneCacheEntry
              pruneCacheEntry(cache, key, keys, _vnode);
            }
          }
        }
      }
    
      function pruneCacheEntry (
        cache,
        key,
        keys,
        current
      ) {
        // 通过cached$$1 = cache[key]` 获取头部数据对应的值 `vnode`,执行 `cached$$1.componentInstance.$destroy() 将组件实例销毁
        var entry = cache[key];
        if (entry && (!current || entry.tag !== current.tag)) {
          // 销毁vnode对应的组件实例
          entry.componentInstance.$destroy();
        }
        // 清空组件对应的缓存节点
        cache[key] = null;
        // 删除缓存中的头部数据 keys[0]
        remove(keys, key);
      }
    
      var patternTypes = [String, RegExp, Array];
    
      var KeepAlive = {
        name: 'keep-alive',
        // 抽象组件,判断当前组件虚拟dom是否渲染成真实dom的关键
        abstract: true,
      // 定义include、exclude及max属性
      // include - 字符串或正则表达式。只有名称匹配的组件会被缓存。
      // exclude - 字符串或正则表达式。任何名称匹配的组件都不会被缓存。
      // max - 数字。最多可以缓存多少组件实例。
        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;
            }
          }
        },
        //在 keep-alive 的创建阶段, created钩子会创建一个cache对象,用来保存vnode节点
        created: function created () {
          this.cache = Object.create(null);
          this.keys = [];
        },
        // 在销毁阶段,destroyed 钩子则会调用pruneCacheEntry方法清除cache缓存中的所有组件实例
        destroyed: function destroyed () {
          for (var key in this.cache) {
            // 销毁vnode对应的组件实例
            pruneCacheEntry(this.cache, key, this.keys);
          }
        },
    
        mounted: function mounted () {
          var this$1 = this;
    
          this.cacheVNode();
          // 通过 watch 来监听 include 和 exclude,在其改变时调用 pruneCache 以修改 cache 缓存中的缓存数据
          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;
          // 得到slot插槽中的第一个组件
          var vnode = getFirstComponentChild(slot);
          var componentOptions = vnode && vnode.componentOptions;
          if (componentOptions) {
            //获取组件名称,优先获取组件的name字段,否则是组件的tag
            var name = getComponentName(componentOptions);
            var ref = this;
            var include = ref.include;
            var exclude = ref.exclude;
            if (
             // 不需要缓存,则返回 vnode
              (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
            // 同一个构造函数可能会被注册为不同的本地组件,所以单独使用cid是不够的
              ? 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 {
               // 无缓存则创建缓存
              this.vnodeToCache = vnode;
              this.keyToCache = key;
            }
              // keepAlive标记
            vnode.data.keepAlive = true;
          }
          return vnode || (slot && slot[0])
        }
      };
    
      var builtInComponents = {
        KeepAlive: KeepAlive
      };
    
      // 初始化全局API
      function initGlobalAPI (Vue) {
        // config
        var configDef = {};
        // 给configDef添加了一个get属性,这个属性返回得是一个config对象,这个cofig对象里面,有n个属性
        configDef.get = function () { return config; };
        {
          // 给configDef添加了一个set属性,返回的一个警告
          configDef.set = function () {
            warn(
              'Do not replace the Vue.config object, set individual fields instead.'
            );
          };
        }
        // 为Vue的构造函数,添加一个要通过Object.defineProperty监听的属性config
        Object.defineProperty(Vue, 'config', configDef);
    
        // 公开util
        // 设置了一个公开的util对象,但是它不是公共的api,避免依赖
        Vue.util = {
          warn: warn,
          extend: extend,
          mergeOptions: mergeOptions,
          defineReactive: defineReactive$$1
        };
        // 绑定全局API——Vue.set,Vue.delete,Vue.nextTick
        Vue.set = set;
        Vue.delete = del;
        Vue.nextTick = nextTick;
    
        // 2.6 explicit observable API
        Vue.observable = function (obj) {
          observe(obj);
          return obj
        };
    
        Vue.options = Object.create(null);
        ASSET_TYPES.forEach(function (type) {
          Vue.options[type + 's'] = Object.create(null);
        });
    
        // 这用于标识“基”构造函数,以便在Weex的多实例场景中扩展所有普通对象组件
        Vue.options._base = Vue;
    
        extend(Vue.options.components, builtInComponents);
        // 初始化Vue.extend,Vue.mixin,Vue.extend
        // AssetRegisters就是component,directive,filter三者
        initUse(Vue);
        initMixin$1(Vue);
        initExtend(Vue);
        initAssetRegisters(Vue);
      }
    
      initGlobalAPI(Vue);
      // vue.prototype上挂载$isServer、$ssrContext、FunctionalRenderContext
      Object.defineProperty(Vue.prototype, '$isServer', {
        get: isServerRendering
      });
    
      Object.defineProperty(Vue.prototype, '$ssrContext', {
        get: function get () {
          /* istanbul ignore next */
          return this.$vnode && this.$vnode.ssrContext
        }
      });
    
      //为ssr运行时助手安装暴露FunctionalRenderContext
      Object.defineProperty(Vue, 'FunctionalRenderContext', {
        value: FunctionalRenderContext
      });
      // 在vue 上挂载 version 属性
      Vue.version = '2.6.14';
      //检验是否存在'style','class'字符串
      var isReservedAttr = makeMap('style,class');
    
      // 应该使用props进行绑定的属性
      //检验是否存在'input','textarea','option','select','progress'字符串
      var acceptValue = makeMap('input,textarea,option,select,progress');
      //校验元素值是否通过prop指定
      var mustUseProp = function (tag, type, attr) {
        return (
          (attr === 'value' && acceptValue(tag)) && type !== 'button' ||
          (attr === 'selected' && tag === 'option') ||
          (attr === 'checked' && tag === 'input') ||
          (attr === 'muted' && tag === 'video')
        )
      };
    //检验是否存在'contenteditable','draggable','spellcheck'字符串
      var isEnumeratedAttr = makeMap('contenteditable,draggable,spellcheck');
    //检验是否存在'events','caret','typing','plaintext-only'字符串
      var isValidContentEditableValue = makeMap('events,caret,typing,plaintext-only');
    
      var convertEnumeratedValue = function (key, value) {
        return isFalsyAttrValue(value) || value === 'false'
          ? 'false'
          // 允许content - itable的任意字符串值
          : key === 'contenteditable' && isValidContentEditableValue(value)
            ? value
            : 'true'
      };
    //校验是否包含以下字符串
      var isBooleanAttr = makeMap(
        'allowfullscreen,async,autofocus,autoplay,checked,compact,controls,declare,' +
        'default,defaultchecked,defaultmuted,defaultselected,defer,disabled,' +
        'enabled,formnovalidate,hidden,indeterminate,inert,ismap,itemscope,loop,multiple,' +
        'muted,nohref,noresize,noshade,novalidate,nowrap,open,pauseonexit,readonly,' +
        'required,reversed,scoped,seamless,selected,sortable,' +
        'truespeed,typemustmatch,visible'
      );
    
      var xlinkNS = 'http://www.w3.org/1999/xlink';
    
      var isXlink = function (name) {
        return name.charAt(5) === ':' && name.slice(0, 5) === 'xlink'
      };
    
      var getXlinkProp = function (name) {
        return isXlink(name) ? name.slice(6, name.length) : ''
      };
      // 通过isFalsyAttrValue方法判断值是否是false与null,如果isFalsyAttrValue返回为true则表示传入的值
      var isFalsyAttrValue = function (val) {
        return val == null || val === false
      };
    
      /* class 转码获取vonde 中的staticClass 静态class 和class动态class转义成真实dom需要的class格式。然后返回class字符串 */
      function genClassForVnode (vnode) {
        var data = vnode.data;
        var parentNode = vnode;
        var childNode = vnode;
        while (isDef(childNode.componentInstance)) {
          childNode = childNode.componentInstance._vnode;
          if (childNode && childNode.data) {
            data = mergeClassData(childNode.data, data);
          }
        }
        while (isDef(parentNode = parentNode.parent)) {
          if (parentNode && parentNode.data) {
            data = mergeClassData(data, parentNode.data);
          }
        }
        // 
        return renderClass(data.staticClass, data.class)
      }
    
      function mergeClassData (child, parent) {
        return {
          staticClass: concat(child.staticClass, parent.staticClass),
          class: isDef(child.class)
            ? [child.class, parent.class]
            : parent.class
        }
      }
      // 渲染calss 这里获取到已经转码的calss
      function renderClass (
        staticClass,
        dynamicClass
      ) {
        if (isDef(staticClass) || isDef(dynamicClass)) {
          // 转码 class,把数组格式,对象格式的calss 全部转化成 字符串格式
          return concat(staticClass, stringifyClass(dynamicClass))
        }
        /* istanbul ignore next */
        return ''
      }
    
      function concat (a, b) {
        return a ? b ? (a + ' ' + b) : a : (b || '')
      }
      // 转码 class,把数组格式,对象格式的calss 全部转化成 字符串格式
      function stringifyClass (value) {
        if (Array.isArray(value)) {
          // 数组字符串变成字符串,然后用空格 隔开 拼接 起来变成字符串
          return stringifyArray(value)
        }
        if (isObject(value)) {
          // 对象字符串变成字符串,然后用空格 隔开 拼接 起来变成字符串
          return stringifyObject(value)
        }
        if (typeof value === 'string') {
          return value
        }
        /* istanbul ignore next */
        return ''
      }
      // 数组字符串变成字符串,然后用空格 隔开 拼接 起来变成字符串
      function stringifyArray (value) {
        var res = '';
        var stringified;
        for (var i = 0, l = value.length; i < l; i++) {
          if (isDef(stringified = stringifyClass(value[i])) && stringified !== '') {
            if (res) { res += ' '; }
            res += stringified;
          }
        }
        return res
      }
    // 对象字符串变成字符串,然后用空格 隔开 拼接 起来变成字符串
      function stringifyObject (value) {
        var res = '';
        for (var key in value) {
          if (value[key]) {
            if (res) { res += ' '; }
            res += key;
          }
        }
        return res
      }
    
      /*  */
    
      var namespaceMap = {
        svg: 'http://www.w3.org/2000/svg',
        math: 'http://www.w3.org/1998/Math/MathML'
      };
    //判断html标签
      var isHTMLTag = makeMap(
        'html,body,base,head,link,meta,style,title,' +
        'address,article,aside,footer,header,h1,h2,h3,h4,h5,h6,hgroup,nav,section,' +
        'div,dd,dl,dt,figcaption,figure,picture,hr,img,li,main,ol,p,pre,ul,' +
        'a,b,abbr,bdi,bdo,br,cite,code,data,dfn,em,i,kbd,mark,q,rp,rt,rtc,ruby,' +
        's,samp,small,span,strong,sub,sup,time,u,var,wbr,area,audio,map,track,video,' +
        'embed,object,param,source,canvas,script,noscript,del,ins,' +
        'caption,col,colgroup,table,thead,tbody,td,th,tr,' +
        'button,datalist,fieldset,form,input,label,legend,meter,optgroup,option,' +
        'output,progress,select,textarea,' +
        'details,dialog,menu,menuitem,summary,' +
        'content,element,shadow,template,blockquote,iframe,tfoot'
      );
    
      //判断svg 标签
      var isSVG = makeMap(
        'svg,animate,circle,clippath,cursor,defs,desc,ellipse,filter,font-face,' +
        'foreignobject,g,glyph,image,line,marker,mask,missing-glyph,path,pattern,' +
        'polygon,polyline,rect,switch,symbol,text,textpath,tspan,use,view',
        true
      );
      // 判断tag是不是pre
      var isPreTag = function (tag) { return tag === 'pre'; };
     // 判断tag是不是html标签或者svg标签
      var isReservedTag = function (tag) {
        return isHTMLTag(tag) || isSVG(tag)
      };
      function getTagNamespace (tag) {
        if (isSVG(tag)) {
          return 'svg'
        }
        // 对MathML的基本支持注意,它不支持其他MathML元素作为组件根
        if (tag === 'math') {
          return 'math'
        }
      }
    
      var unknownElementCache = Object.create(null);
      // 检查dom 节点的tag标签 类型 是否是VPre 标签 或者是判断是否是浏览器自带原有的标签
      function isUnknownElement (tag) {
        /* istanbul ignore if */
        if (!inBrowser) {
          return true
        }
        // 判断tag是不是html标签或者svg标签
        if (isReservedTag(tag)) {
          return false
        }
        tag = tag.toLowerCase();
        /* istanbul ignore if */
        if (unknownElementCache[tag] != null) {
          return unknownElementCache[tag]
        }
        var el = document.createElement(tag);
        if (tag.indexOf('-') > -1) {
          // http://stackoverflow.com/a/28210364/1070244
          return (unknownElementCache[tag] = (
            el.constructor === window.HTMLUnknownElement ||
            el.constructor === window.HTMLElement
          ))
        } else {
          return (unknownElementCache[tag] = /HTMLUnknownElement/.test(el.toString()))
        }
      }
    
      var isTextInputType = makeMap('text,number,password,search,email,tel,url');
    
      /*  */
    
      /**
       * 查询一个元素选择器,如果他不是元素选择器则创建div
       */
      function query (el) {
        if (typeof el === 'string') {
          var selected = document.querySelector(el);
          if (!selected) {
            warn(
              'Cannot find element: ' + el
            );
            return document.createElement('div')
          }
          return selected
        } else {
          return el
        }
      }
    
      //创建一个真实的dom
      function createElement$1 (tagName, vnode) {
        var elm = document.createElement(tagName);
        if (tagName !== 'select') {
          return elm
        }
        // False或null将删除该属性,但undefined不会
        if (vnode.data && vnode.data.attrs && vnode.data.attrs.multiple !== undefined) {
          elm.setAttribute('multiple', 'multiple');
        }
        return elm
      }
      // 创建由tagName 指定的HTML元素
      function createElementNS (namespace, tagName) {
        return document.createElementNS(namespaceMap[namespace], tagName)
      }
      // 创建文本节点
      function createTextNode (text) {
        return document.createTextNode(text)
      }
      // 创建一个注释节点
      function createComment (text) {
        return document.createComment(text)
      }
      // 插入节点 在某个元素前面插入一个节点
      function insertBefore (parentNode, newNode, referenceNode) {
        parentNode.insertBefore(newNode, referenceNode);
      }
      // 删除子节点
      function removeChild (node, child) {
        node.removeChild(child);
      }
      //添加子节点 尾部
      function appendChild (node, child) {
        node.appendChild(child);
      }
      // 获取父亲子节点dom
      function parentNode (node) {
        return node.parentNode
      }
      //获取下一个兄弟节点
      function nextSibling (node) {
        return node.nextSibling
      }
      //获取dom标签名称
      function tagName (node) {
        return node.tagName
      }
       //设置dom 文本
      function setTextContent (node, text) {
        node.textContent = text;
      }
      //设置组建样式的作用域
      function setStyleScope (node, scopeId) {
        node.setAttribute(scopeId, '');
      }
      // Object.freeze冻结对象
      var nodeOps = /*#__PURE__*/Object.freeze({
        createElement: createElement$1,
        createElementNS: createElementNS,
        createTextNode: createTextNode,
        createComment: createComment,
        insertBefore: insertBefore,
        removeChild: removeChild,
        appendChild: appendChild,
        parentNode: parentNode,
        nextSibling: nextSibling,
        tagName: tagName,
        setTextContent: setTextContent,
        setStyleScope: setStyleScope
      });
    
      /* ref是给元素或者子组件注册引用信息的 */
    
      var ref = {
        create: function create (_, vnode) {
          registerRef(vnode);
        },
        update: function update (oldVnode, vnode) {
          if (oldVnode.data.ref !== vnode.data.ref) {
            registerRef(oldVnode, true);
            registerRef(vnode);
          }
        },
        destroy: function destroy (vnode) {
          registerRef(vnode, true);
        }
      };
      // ref模块初始化时会执行registerRef函数
      function registerRef (vnode, isRemoval) {
        var key = vnode.data.ref;
        //如果没有定义ref属性,则直接返回
        if (!isDef(key)) { return }
        //当前的根Vue实例
        var vm = vnode.context;
         //优先获取vonde的组件实例(对于组件来说),或者el(该Vnode对应的DOM节点,非组件来说)
        var ref = vnode.componentInstance || vnode.elm;
        var refs = vm.$refs;
        if (isRemoval) {
          if (Array.isArray(refs[key])) {
            remove(refs[key], ref);
          } else if (refs[key] === ref) {
            refs[key] = undefined;
          }
        } else {
           //如果不是移除,当在v-for之内时,则保存为数组形式
          if (vnode.data.refInFor) {
            if (!Array.isArray(refs[key])) {
              refs[key] = [ref];
            } else if (refs[key].indexOf(ref) < 0) {
              // $flow-disable-line
              refs[key].push(ref);
            }
             //不是在v-for之内时,直接保存到refs对应的key属性上
          } else {
            refs[key] = ref;
          }
        }
      }
    
      /**
       * Virtual DOM patching algorithm based on Snabbdom by
       * Simon Friis Vindum (@paldepind)
       * Licensed under the MIT License
       * https://github.com/paldepind/snabbdom/blob/master/LICENSE
       *
       * modified by Evan You (@yyx990803)
       *
       * Not type-checking this because this file is perf-critical and the cost
       * of making flow understand it is not worth it.
       */
    
      var emptyNode = new VNode('', {}, []);
    
      var hooks = ['create', 'activate', 'update', 'remove', 'destroy'];
      // 判断当前VNode可复用,销毁一个DOM节点并创建一个新的再插入是消耗非常大的,
      // 无论是DOM对象本身的复杂性还是操作引起的重绘重排,所以虚拟DOM的目标是尽可能复用现有DOM进行更新
      function sameVnode (a, b) {
        return (
          a.key === b.key &&
          a.asyncFactory === b.asyncFactory && (
            (
              a.tag === b.tag &&
              a.isComment === b.isComment &&
              // 这个涉及属性的更新,如果一个节点没有任何属性,即data为undefined,与一个有data属性的节点进行更新不如直接渲染一个新的
              isDef(a.data) === isDef(b.data) &&
              // 这个主要是input标签type属性异同判断,不同的type相当于不同的tag
              sameInputType(a, b)
            ) || (
              isTrue(a.isAsyncPlaceholder) &&
              isUndef(b.asyncFactory.error)
            )
          )
        )
      }
      //这个主要是input标签type属性异同判断,不同的type相当于不同的tag
      function sameInputType (a, b) {
        if (a.tag !== 'input') { return true }
        var i;
        var typeA = isDef(i = a.data) && isDef(i = i.attrs) && i.type;
        var typeB = isDef(i = b.data) && isDef(i = i.attrs) && i.type;
        return typeA === typeB || isTextInputType(typeA) && isTextInputType(typeB)
      }
      // 接收一个 children 数组,生成 key 与 index 索引对应的一个 map 表
      function createKeyToOldIdx (children, beginIdx, endIdx) {
        var i, key;
        var map = {};
        for (i = beginIdx; i <= endIdx; ++i) {
          key = children[i].key;
          if (isDef(key)) { map[key] = i; }
        }
        return map
      }
      // patch核心函数
      // backend的nodeOps是节点的功能函数,包括createElement创建元素、removeChild删除子元素,
      // tagName获取到标签名等,backend的modules是vue框架用于分别执行某个渲染任务的功能函数
      function createPatchFunction (backend) {
        var i, j;
        var cbs = {};
    
        var modules = backend.modules;
        var nodeOps = backend.nodeOps;
        // 循环hooks和modules
        for (i = 0; i < hooks.length; ++i) {
          cbs[hooks[i]] = [];
          for (j = 0; j < modules.length; ++j) {
            if (isDef(modules[j][hooks[i]])) {
              cbs[hooks[i]].push(modules[j][hooks[i]]);
            }
          }
        }
        // 将原有的节点,同时也是DOM节点包装成虚拟节点
        function emptyNodeAt (elm) {
          return new VNode(nodeOps.tagName(elm).toLowerCase(), {}, [], undefined, elm)
        }
        // 创建remove函数
        // remove$$1函数作为一个对象,第一个参数是vnode所属的dom元素,第二个参数是监听器个数。
        // 内部实现remove函数拥有listeners属性,等到这个属性的值每一次减少直到0时将直接移除节点
        function createRmCb (childElm, listeners) {
          function remove$$1 () {
            if (--remove$$1.listeners === 0) {
              removeNode(childElm);
            }
          }
          remove$$1.listeners = listeners;
          return remove$$1
        }
        // 移除节点,先找到父节点,然后通过removeChild移除掉这个节点
        function removeNode (el) {
          var parent = nodeOps.parentNode(el);
          // 元素可能已经由于v-html / v-text而被删除
          if (isDef(parent)) {
            nodeOps.removeChild(parent, el);
          }
        }
        function isUnknownElement$$1 (vnode, inVPre) {
          return (
            !inVPre &&
            !vnode.ns &&
            !(
              config.ignoredElements.length &&
              config.ignoredElements.some(function (ignore) {
                return isRegExp(ignore)
                  ? ignore.test(vnode.tag)
                  : ignore === vnode.tag
              })
            ) &&
            // 检测是否未知el
            config.isUnknownElement(vnode.tag)
          )
        }
    
        var creatingElmInVPre = 0;
        // 创建新节点
        function createElm (
          vnode,
          insertedVnodeQueue,
          parentElm,
          refElm,
          nested,
          ownerArray,
          index
        ) {
          if (isDef(vnode.elm) && isDef(ownerArray)) {
            //这个vnode在以前的渲染中使用过,现在它被用作一个新节点,覆盖它的榆树将导致
            //当它被用作插入时,潜在的补丁错误,引用节点。相反,我们在创建节点之前按需克隆节点对应的DOM元素。
            vnode = ownerArray[index] = cloneVNode(vnode);
          }
    
          vnode.isRootInsert = !nested; //过渡进入检查
          if (createComponent(vnode, insertedVnodeQueue, parentElm, refElm)) {
            return
          }
    
          var data = vnode.data;
          var children = vnode.children;
          var tag = vnode.tag;
          if (isDef(tag)) {
            {
              if (data && data.pre) {
                creatingElmInVPre++;
              }
              if (isUnknownElement$$1(vnode, creatingElmInVPre)) {
                warn(
                  'Unknown custom element: <' + tag + '> - did you ' +
                  'register the component correctly? For recursive components, ' +
                  'make sure to provide the "name" option.',
                  vnode.context
                );
              }
            }
    
            vnode.elm = vnode.ns
              ? nodeOps.createElementNS(vnode.ns, tag)
              : nodeOps.createElement(tag, vnode);
            setScope(vnode);
    
            /* istanbul ignore if */
            {
              createChildren(vnode, children, insertedVnodeQueue);
              if (isDef(data)) {
                invokeCreateHooks(vnode, insertedVnodeQueue);
              }
              insert(parentElm, vnode.elm, refElm);
            }
    
            if (data && data.pre) {
              creatingElmInVPre--;
            }
          } else if (isTrue(vnode.isComment)) {
            vnode.elm = nodeOps.createComment(vnode.text);
            insert(parentElm, vnode.elm, refElm);
          } else {
            vnode.elm = nodeOps.createTextNode(vnode.text);
            insert(parentElm, vnode.elm, refElm);
          }
        }
    
        function createComponent (vnode, insertedVnodeQueue, parentElm, refElm) {
          var i = vnode.data;
          if (isDef(i)) {
            var isReactivated = isDef(vnode.componentInstance) && i.keepAlive;
            if (isDef(i = i.hook) && isDef(i = i.init)) {
              i(vnode, false /* hydrating */);
            }
            // 调用init钩子后,如果vnode是子组件它应该创建一个子实例并挂载它。这个子组件也设置了占位符vnode的榆树。
            //在这种情况下,我们只需要返回元素就可以了。
            if (isDef(vnode.componentInstance)) {
              initComponent(vnode, insertedVnodeQueue);
              insert(parentElm, vnode.elm, refElm);
              if (isTrue(isReactivated)) {
                reactivateComponent(vnode, insertedVnodeQueue, parentElm, refElm);
              }
              return true
            }
          }
        }
    
        function initComponent (vnode, insertedVnodeQueue) {
          if (isDef(vnode.data.pendingInsert)) {
            insertedVnodeQueue.push.apply(insertedVnodeQueue, vnode.data.pendingInsert);
            vnode.data.pendingInsert = null;
          }
          vnode.elm = vnode.componentInstance.$el;
          
          if (isPatchable(vnode)) {
            // div#app的创建时会调用invokeCreateHooks
            invokeCreateHooks(vnode, insertedVnodeQueue);
            setScope(vnode);
          } else {
            // 根空的组件。跳过所有元素相关的模块,除了ref
            registerRef(vnode);
            // 确保调用了插入钩子
            insertedVnodeQueue.push(vnode);
          }
        }
    
        function reactivateComponent (vnode, insertedVnodeQueue, parentElm, refElm) {
          var i;
          // 重新激活的内部转换组件不触发,因为内部节点创建的钩子没有被调用一次。
          var innerNode = vnode;
          while (innerNode.componentInstance) {
            innerNode = innerNode.componentInstance._vnode;
            if (isDef(i = innerNode.data) && isDef(i = i.transition)) {
              for (i = 0; i < cbs.activate.length; ++i) {
                cbs.activate[i](emptyNode, innerNode);
              }
              insertedVnodeQueue.push(innerNode);
              break
            }
          }
          // 与新创建的组件不同,重新激活的keep-alive组件不插入自己
          insert(parentElm, vnode.elm, refElm);
        }
        // 通过insertBefore或者appendChild添加元素
        function insert (parent, elm, ref$$1) {
          if (isDef(parent)) {
            if (isDef(ref$$1)) {
              if (nodeOps.parentNode(ref$$1) === parent) {
                nodeOps.insertBefore(parent, elm, ref$$1);
              }
            } else {
              nodeOps.appendChild(parent, elm);
            }
          }
        }
    

    相关文章

      网友评论

        本文标题:史上最全的vue.js源码解析(四)

        本文链接:https://www.haomeiwen.com/subject/oighiltx.html