执行new Vue时到底发生了什么(一)

代码:vue

<template>
  <div id="app">
    {{message}}
  </div>
</template>

<script>

export default {
  name: 'app',
  data: function() {
    return {
      message: "Hello World"
    }
  }
}
</script>

<style>
#app {
  text-align: center;
  color: #2c3e50;
  font-family: 'Courier New', Courier, monospace;
  font-size: 18px;
}
</style>

结果:
图片描述node

问题:背后发生了什么
阅读源码的第一步是知道如何调试,不会调试就不可能分析出代码的执行逻辑。首先,在项目中咱们引入了Vue:import Vue from 'vue'。问题是vue到底从哪里来的。从node_modules中来。在node_modules路径下存在vue文件夹,vue文件夹中存在一个package.json文件,这个文件的做用是描述整个项目的。在这个文件中存在两个配置字段,它们都是程序的主入口文件。json

"main": "dist/vue.runtime.common.js", 
  "module": "dist/vue.runtime.esm.js",

在module的优先级大于main的优先级。在module不存在时,main对应的配置项就是主入口文件。能够看到
dist/vue.runtime.esm.js才是主入口文件。咱们是经过app

new Vue({
  render: h => h(App),
}).$mount('#app')

建立Vue实例的,所以首先搜索Vue的定义ide

function Vue (options) {
  if (process.env.NODE_ENV !== 'production' &&
    !(this instanceof Vue)
  ) {
    warn('Vue is a constructor and should be called with the `new` keyword');
  }
  this._init(options);
}

能够看到Vue构造函数的核心代码只有一行:this._init(options);所以搜索私有化_init方法。因为_init是做为this的一个方法,注意此处的this就是Vue。通过查找_init方法的定义以下:函数

Vue.prototype._init = function (options) {
    var vm = this;
    // a uid
    vm._uid = uid$3++;

    var startTag, endTag;
    /* istanbul ignore if */
    if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
      startTag = "vue-perf-start:" + (vm._uid);
      endTag = "vue-perf-end:" + (vm._uid);
      mark(startTag);
    }

    // a flag to avoid this being observed
    vm._isVue = true;
    // merge 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 */
    if (process.env.NODE_ENV !== 'production') {
      initProxy(vm);
    } else {
      vm._renderProxy = vm;
    }
    // expose real self
    vm._self = vm;
    debugger
    initLifecycle(vm);
    initEvents(vm);
    initRender(vm);
    callHook(vm, 'beforeCreate');
    initInjections(vm); // resolve injections before data/props
    initState(vm);
    initProvide(vm); // resolve provide after data/props
    callHook(vm, 'created');

    /* istanbul ignore if */
    if (process.env.NODE_ENV !== 'production' && 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);
    }
  };
}

读源码须要注意的一点就是不相关的必定要忽略,一旦遵循深度遍历法则读下去,你是必定会失败的。若是方法不对,那还不如不读,睡会觉。能够将上面的代码简化为:ui

Vue.prototype._init = function (options) {
    var vm = this;
    ...
    vm.$options = mergeOptions(options || {}, vm);
    ...
    initState(vm);
    ...
    if (vm.$options.el) {
      vm.$mount(vm.$options.el);
    }
    ...
  };
}

_init方法整体上作的事情其实并很少,第一项就是合并配置项。好比路由,状态管理,渲染函数。this

new Vue({
  store: store,
  router: router,
  render: h => h(App),
}).$mount('#app')

而后初始化状态,initState的方法定义以下:spa

function initState (vm) {
  vm._watchers = [];
  var opts = vm.$options;
  if (opts.props) { initProps(vm, opts.props); }
  if (opts.methods) { initMethods(vm, opts.methods); }
  if (opts.data) {
    initData(vm);
  } else {
    observe(vm._data = {}, true /* asRootData */);
  }
  if (opts.computed) { initComputed(vm, opts.computed); }
  if (opts.watch && opts.watch !== nativeWatch) {
    initWatch(vm, opts.watch);
  }
}

从源码能够看出,initState就是将vue实例中的data,method,computed,watch等数据项作进一步得处理,其实就是作代理以及转化成可观测对象。
数据处理完成以后就将数据挂载到指定的钩子上:vm.$mount(vm.$options.el);prototype

另外须要注意的是,_init方法中有一下一段代码,在上面我为来突出主线而省略了,这就是

initLifecycle(vm);
    initEvents(vm);
    initRender(vm);
    callHook(vm, 'beforeCreate');
    initInjections(vm); // resolve injections before data/props
    initState(vm);
    initProvide(vm); // resolve provide after data/props
    callHook(vm, 'created');

能够看到在initState(vm)执行以前,咱们执行了beforeCreate方法,在initState(vm)执行以后,咱们执行了created方法。所以在beforeCreate方法中,咱们没法直接引用data,method,computed,watch等在initState(vm)中才开始存在的属性。

相关文章
相关标签/搜索