我们可以通过Vue对象中的this.xxx来访问Vue中data对象中定义的数据,但是为什么可以直接使用this.xxx来访问呢?我们可以通过查看源代码来理解它。当我们使用new创建一个Vue实例时,实际上调用了一个Vue方法。/*src/core/instance/index.js*/functionVue(options){if(process.env.NODE_ENV!=='production'&&!(thisinstanceofVue)){warn('Vue是一个构造函数,应该用`new`关键字调用')}this._init(options)}初始化的时候主要是在Vue方法中调用_init方法。这个_init方法其实是一个挂在Vue原型上的方法。在这个文件中还包含一行代码initMixin(Vue),就是完成一个在Vue原型上挂载_init方法的操作。然后我们输入_init(options)查看执行了哪些操作。这时进入src/core/instance/init.js找到initMixin方法。在initMixin的开头可以看到Vue.prototype._init=function(){}这是Vue对象原型上的_init方法。那我们看看这个方法中进行了哪些操作?此时忽略一些细节,直接看到有一个充满initXXX的函数调用。这块大致是Vue对象初始化的一些流程,比如生命周期,事件等等。/*src/core/instance/init.js*/exportfunctioninitMixin(Vue:Class){Vue.prototype._init=function(options?:Object){/*更多细节*/vm._self=vminitLifecycle(vm)initEvents(vm)initRender(vm)callHook(vm,'beforeCreate')initInjections(vm)//解决数据/道具前的注入initState(vm)initProvide(vm)//解决数据/道具后的提供callHook(vm,'created')/*moredetails*/}}数据的处理主要集中在这个initState方法上。这个文件中引入了initState方法,所以我们找到真正定义了initState方法的src/core/instance/state.js。initState定义的结构看起来很简单,就是根据我们选项上的不同属性做相应的初始化处理。/*src/core/instance/state.js*/exportfunctioninitState(vm:Component){vm._watchers=[]constopts=vm.$optionsif(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)}}我们的主要目标仍然是数据。这个时候我们主要看下面进行的数据属性操作。data选项存在时调用initData,不存在时调用observer,data赋值为空对象。Observer是和数据响应相关的,这里就不细说了。/*src/core/instance/state.js*/functioninitData(vm:Component){letdata=vm.$options.datadata=vm._data=typeofdata==='功能'?getData(数据,虚拟机):数据||{}if(!isPlainObject(data)){data={}process.env.NODE_ENV!=='production'&&warn('datafunctionsshouldreturnanobject:n'+'',vm)}//在实例上代理数据constkeys=Object.keys(data)constprops=vm.$options.propsconstmethods=vm.$options.methodsleti=keys.lengthwhile(i--){constkey=keys[i]if(process.env.NODE_ENV!=='production'){if(methods&&hasOwn(methods,键)){warn(`方法"${key}"已经被定义为数据属性。`,vm)}}if(props&&hasOwn(props,key)){process.env.NODE_ENV!=='production'&&warn(`数据属性“${key}”已经声明为道具。`+`使用道具默认值代替。`,vm)}elseif(!isReserved(key)){proxy(vm,`_data`,key)}}//observedataobserve(data,true/*asRootData*/)}initData的操作分为三部分:判断data是否为函数数据代理响应(暂不详述)我们在创建一个When创建一个新的Vue对象,data是一个函数,它返回一个具有已定义参数的新对象。如果不是函数,会报错。具体原因与组件有关。newVue({data(){return{message:'Helloworld',}},});newVue({data:{message:'Helloworld',},});确保data在dataagent中的属性名不能和props、method、reserved属性重名,因为这些最终都会挂载到vue实例上。代理的实现部分取决于代理方法。/*src/core/instance/state.js*/exportfunctionproxy(target:Object,sourceKey:string,key:string){sharedPropertyDefinition.get=functionproxyGetter(){returnthis[sourceKey][key]}sharedPropertyDefinition.set=functionproxySetter(val){this[sourceKey][key]=val}Object.defineProperty(target,key,sharedPropertyDefinition)}通过设置对象上访问器属性的get和set,我们可以访问vm.xxx时间访问vm._data.xxx。vm的_data属性是initData中判断data是否为函数时的赋值操作。这样,当我们访问vm.xxx时,实际上是在访问我们定义的数据上的属性。