執行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)中才開始存在的屬性。

相關文章
相關標籤/搜索