乐趣区

关于vue.js:Vuejs源码学习Vue对象在data中定义的属性为什么可以直接通过thisxxx访问

咱们在 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 上的属性了。

退出移动版