https://github.com/vuejs/vuejavascript
从github地址,直接download下来就好了。在新建项目的时候也能够node_modelus里的vue搭配着看。html
首先先引入vue,而后新建他的实例。vue
import Vue from 'vue' var app = new Vue({ el:'#app', data:{ return { message:"hello world!" } } })
首先咱们得知道咱们引入 的是个什么东西。因此咱们找到源码./src/core/instance/index.js
里,找到了vue的庐山真面目了,其实vue就是一个类。java
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) }
首先process.env.NODE_ENV
是判断你启动时候的参数的,若是不符合的话,就发出警告,不然执行_init
方法。值得一提的是通常属性名前面加_
默认表明是私有属性,不对外展现。固然若是你打印vue实例的话仍是能看见,由于只是_
是私有属性人们约定俗成的,没有js语言层面的私有。node
那么这个_init
是哪来的呢?往下看:git
initMixin(Vue) stateMixin(Vue) eventsMixin(Vue) lifecycleMixin(Vue) renderMixin(Vue)
能够看到下面有一大串Mixin,咱们挑第一个initMixin,而后去查看他的定义。vscode能够直接右键,而后选择转到定义 或者直接command加鼠标左键点击函数名称就能够跳过去看到定义这个方法的地方。github
export function initMixin(Vue: Class<Component>) { Vue.prototype._init = function (options?: Object) { const vm: Component = this // a uid vm._uid = uid++ //.. // 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 initLifecycle(vm) initEvents(vm) initRender(vm) callHook(vm, 'beforeCreate') initInjections(vm) // resolve injections before data/props initState(vm) // .. if (vm.$options.el) { vm.$mount(vm.$options.el) } } }
init就在最开头app
Vue.prototype._init = function (options?: Object) { const vm: Component = this // a uid vm._uid = uid++ //.. // 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 ) }
init具体包括啥呢,首先将this上下文传给vm这个对象,而后设置_uid
而后再机型一系列的初始化的工做。而后再合并options,最后挂载到vm上。
可能有人会好奇,在形参部分,Vue: Class<Component>
是什么意思,由于JavaScript是一个动态类型语言,也就是说,声明变量的时候不会指派他是任何一种类型的语言,像java就是典型的静态类型语言。例如:boolean result = true
就是声明result是一个布尔类型,而相对的,JavaScript中能够声明var result =true
。这样虽然方便不少,可是由于静态类型在编译过程当中就会查出错误并提示开发者改正错误,可是像Js这样的动态语言在编译的时候既是存在错误也不会提出,只有在真正运行时才会出错。因此就会有没必要要的麻烦,那么如何对Js进行静态类型检查呢?就是插件呗。vue用了flow的插件,让js有了静态类型检查,:
后面表明了限定vue这个形参的属性。具体就不展开了,能够去看flow的文档。ide
Flow:https://flow.org/函数
接下来接着说正文,const vm: Component = this
能够看到把当前的执行先后文给了vm。而后以后就是一些陆陆续续的挂载,值得注意的就是vm.$options
就是填写在vue实例里的参数,例如el
,mounted
,data
都被保存在$options
里。
可是日常使用的时候咱们没有用到this.$options.data1
里,反而是直接用this.data1
来调用,这其实vue也在其中进行了操做。
咱们会发如今上面的代码段里有一行initState(vm)
,咱们找到initState的定义。
export function initState (vm: Component) { // .. const opts = vm.$options if (opts.data) { initData(vm) } // .. }
而后咱们能够接着转到initData这个方法的定义
function initData (vm: Component) { let data = vm.$options.data data = vm._data = typeof data === 'function' ? getData(data, vm) : data || {} if (!isPlainObject(data)) { data = {} process.env.NODE_ENV !== 'production' && 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 const keys = Object.keys(data) const props = vm.$options.props const methods = vm.$options.methods let i = keys.length while (i--) { const key = keys[i] if (process.env.NODE_ENV !== 'production') { if (methods && hasOwn(methods, key)) { warn( `Method "${key}" has already been defined as a data property.`, vm ) } } if (props && hasOwn(props, key)) { process.env.NODE_ENV !== 'production' && 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 */) }
把上面的代码拆分来看
let data = vm.$options.data data = vm._data = typeof data === 'function' ? getData(data, vm) : data || {}
上面代码先经过$options获取到data,而后判断data是否是经过返回对象的方式创建的,若是是,那么则执行getData方法。getData的方法主要操做就是 data.call(vm, vm)
这步经过给data调用了vm这个上下文环境,而后直接返回这个包括data的vm对象。
那么如今vm上已经有data了是吗?确实,可是这个data是vm._data
也就是说若是你想访问message
这个属性你如今只能经过vue._data.message
这样来访问。因此咱们接着往下看。
// proxy data on instance const keys = Object.keys(data) const props = vm.$options.props const methods = vm.$options.methods let i = keys.length while (i--) { const key = keys[i] if (process.env.NODE_ENV !== 'production') { if (methods && hasOwn(methods, key)) { warn( `Method "${key}" has already been defined as a data property.`, vm ) } } if (props && hasOwn(props, key)) { process.env.NODE_ENV !== 'production' && 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) } }
这一大段上面聚焦的是prop data methods 们若是相同以后就会提出相应的警示。为何要他们不同呢,由于他们都是经过this.XX来调用的,若是重名,vue分不清他们是谁。若是都没问题了,咱们就把_datas上的值直接赋给vm,而后转到最后一步proxy(vm, _data, key)
,而后咱们转移到proxy这个方法中:
const sharedPropertyDefinition = { enumerable: true, configurable: true, get: noop, set: noop } export function proxy (target: Object, sourceKey: string, key: string) { sharedPropertyDefinition.get = function proxyGetter () { return this[sourceKey][key] } sharedPropertyDefinition.set = function proxySetter (val) { this[sourceKey][key] = val } Object.defineProperty(target, key, sharedPropertyDefinition) }
就是经过sharedPropertyDefinition.get
和sharedPropertyDefinition.set
的设置的get和set方法,而后在经过Object.defineProperty
来定义访问target.key
的时候调用sharedPropertyDefinition
的set和get。
也就是至关于,我要求vm.message
,就会触发sharedPropertyDefinition
的get,而后返回vm._data.message
至此数据就能够经过vm.message
的方式访问了。