Vue 生命周期created之后执行了什么

446 阅读2分钟

created之后的生命周期

想看结论的直接往下翻,这文章又臭又长。

我们先看Vue的生命周期示图

在created之后就是选择el所用的DOM,并编译到render函数中(render函数就是用于编译生成Vnode的)。

但render真的是在created到beforeMount阶段调用的么

created到beforMount

callHook(vm, 'created');
...
vm.$mount(vm.$options.el);
在调用created后会调用vm.$mount,如果用户没有设定el那么会开始阻塞,直到用户手动调用vm.$mount为止。

  Vue.prototype.$mount = function (
    el,
    hydrating
  ) {
    el = el && inBrowser ? query(el) : undefined; //返回Template或者指定ID标签的DOM
    return mountComponent(this, el, hydrating)
  };
  el最终会返回Template或者HTML的DOM
  之后执行mountComponent,this指向Vue的实例、el为DOM元素、hydrating则是undefined

我们在看看

function mountComponent (
  vm,
  el,
  hydrating
) {
  vm.$el = el;
  if (!vm.$options.render) {
    vm.$options.render = createEmptyVNode;
    {
      /* istanbul ignore if */
      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
        );
      }
    }
  }
  callHook(vm, 'beforeMount');
  ...
}
好了,简化一下去除检测的
function mountComponent (vm,el,hydrating) {
  vm.$el = el;
  if (!vm.$options.render) {
    vm.$options.render = createEmptyVNode;
  }
  callHook(vm, 'beforeMount');
  ...
}
是不是觉得 vm.$options.render = createEmptyVNode 就是执行了render
其实vm.$options.render就等于createEmptyVNode函数,还没执行
之后callHook(vm, 'beforeMount')

beforeMount结束了

beforeMount结束了,就是把获取到的DOM赋值给了el

beforeMount执行了什么

我们在看看beforeMount做了什么

function beforeMount () {
    var this$1 = this;

    var update = this._update;
    this._update = function (vnode, hydrating) {
      var restoreActiveInstance = setActiveInstance(this$1);
      // force removing pass
      this$1.__patch__(
        this$1._vnode,
        this$1.kept,
        false, // hydrating
        true // removeOnly (!important, avoids unnecessary moves)
      );
      this$1._vnode = this$1.kept;
      restoreActiveInstance();
      update.call(this$1, vnode, hydrating);
    };
  }
  patch就是Vue的diff算法,返回一个新的VNode。
  之后update更新组件。
  但beforeMount只是修改了vm上的update方法,并没有执行。

所以到beforeMount执行完成,都没有进行render渲染

beforMount到Mounted

callHook(vm, 'beforeMount');

  var updateComponent;
  /* istanbul ignore if */
  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(("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);
    };
  }

  // we set this to vm._watcher inside the watcher's constructor
  // since the watcher's initial patch may call $forceUpdate (e.g. inside child
  // component's mounted hook), which relies on vm._watcher being already defined
  new Watcher(vm, updateComponent, noop, {
    before: function before () {
      if (vm._isMounted && !vm._isDestroyed) {
        callHook(vm, 'beforeUpdate');
      }
    }
  }, true /* isRenderWatcher */);
  hydrating = false;

  // manually mounted instance, call mounted on self
  // mounted is called for render-created child components in its inserted hook
  if (vm.$vnode == null) {
    vm._isMounted = true;
    callHook(vm, 'mounted');
  }
  return vm
  
  beforeMount到mounted有关键的两步
  1,updateComponent=的函数,这里if中判断计时器能不能用,用来计时虚拟DOM的生成时间
    updateComponent=function (){vm._update(vm._render(), hydrating);}
    这段是关键
  2,Watcher,源码就不贴了。我总结一下这里的Watcher怎么执行的
    2.1 Watcher的getter属性等于updateComponent,等Watcher触发是调用updateComponent函数
    2.2 this.value = this.lazy ? undefined : this.get();
        lazy只有Watcher监听计算属性时才是true
    2.3 这个Watcher存入了vm._watcher和vm._watchers,一般的Watcher只能存入_watchers
this.get()
源码也不贴了,关键是value = this.getter.call(vm, vm);
在这里执行了vm._update(vm._render(), hydrating)。
实现了render渲染

最后说一点Watcher的before的那个在队列刷新对Watcher排序中使用,只有调用Watcher.update时会调用。git中的地址vue/src/core/observer/scheduler.js 。你可以download用VSCode找会方便,我像个憨批一样翻了git一个小时。

所用到的函数

inBrowser

var inBrowser = typeof window !== 'undefined';
判断是否是浏览器环境

query

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
  }
}
获取Template或HTML的DOM

createEmptyVNode

var createEmptyVNode = function (text) {
  if ( text === void 0 ) text = '';

  var node = new VNode();
  node.text = text;
  node.isComment = true;
  return node
};
创建一个空的VNode

setActiveInstance

var activeInstance = null;
function setActiveInstance(vm) {
  var prevActiveInstance = activeInstance;
  activeInstance = vm;
  return function () {
    activeInstance = prevActiveInstance;
  }
}

总结

render其实在beforeMount到Mounted之间执行,虚拟DOM也在这个阶段生成。

Created到beforeMount,就是将HTML或Template的DOM元素挂载到了el上。

我的帖子可真是又臭又长。