代码: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)中才开始存在的属性。