咱们在Vue对象中能够通过this.xxx去拜访Vue中data对象中定义的数据,然而为什么能够间接应用this.xxx来拜访呢?咱们能够用过看源码的形式来进行了解。

当咱们应用new的形式创立一个Vue实例的时候,实际上调用的是一个Vue的办法。

/* src/core/instance/index.js */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办法中次要调用了_init这个办法。

这个_init办法实际上是一个挂在在Vue原型上的一个办法。同样在这个文件中含有一行代码initMixin(Vue)就是实现了挂载_init办法在Vue原型上的一个操作。

而后咱们进入_init(options)来看进行了哪些操作。这个时候进入src/core/instance/init.js找到initMixin办法。

initMixin一开始就能够看到Vue.prototype._init = function() {} 这个就是Vue对象原型上的_init办法了。而后来看这个办法中进行了哪些操作?这个时候疏忽一些细节,间接看到有一段全是initXXX的函数调用。这一块大抵就是Vue对象初始化的一些流程,比方生命周期,事件等等。

/* src/core/instance/init.js */export function initMixin (Vue: Class<Component>) {  Vue.prototype._init = function (options?: Object) {    /* more details */    vm._self = vm    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')    /* more details */    }} 

对于data的解决就聚焦在这个initState办法上。initState这个办法是引入到这个文件的,所以咱们找到initState这个办法真正定义的中央src/core/instance/state.js

initState定义的构造看起来很简略,就是依据咱们的options上的不同属性来做相应的初始化解决。

/* src/core/instance/state.js */export function initState (vm: Component) {  vm._watchers = []  const 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)  }} 

咱们的次要指标还是data,这个时候咱们咱们就次要去看data属性下进行的操作。data选项存在时候就调用了initData,不存在的时候就调用了observer,同时把data用一个空对象赋值。observer和数据响应式相干,这里先不具体阐明。

接下来就是去看initData做了什么解决了?

/* src/core/instance/state.js */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 */)} 

initData的操作分为三个局部:

  • 判断data是否是一个函数
  • 数据代理
  • 响应式(暂不具体阐明)
  1. 咱们在创立一个新的Vue对象的时候,data是一个函数,返回一个含有定义参数的新对象,如果不是一个函数,就会进行一个报错。具体起因和组件相干。

    new Vue({  data() {   return {      message: 'Hello world',   }    },});new Vue({  data: {    message: 'Hello world',    },}); 
  2. 数据代理的时候要确保data中的属性名称不能和propsmethod和保留属性不能重名,因为最初这些都会挂载在Vue的实例上。代理的实现局部依赖proxy这个办法。

    /* src/core/instance/state.js */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)} 

    通过设置一个对象上的拜访器属性getset,就能使咱们在拜访vm.xxx的时候去拜访vm._data.xxx了。vm_data属性则是在initData中判断data是否使一个函数的时候的赋值操作。这样当咱们去拜访vm.xxx本质上就是拜访的咱们定义的data上的属性了。