vue源码学习总结 this.$data this._data this.$options.data this.xxx之间的联系;data重置

假设有如下代码:

  1. this.$options.data 是demo组件的data函数
  2. this._data 是demo组件的data函数执行结果

看源码src/core/instance/state可知

function initData (vm: Component) {
  let data = vm.$options.data
  data = vm._data = typeof data === 'function'
    ? getData(data, vm)
    : data || {}
}
function getData (data: Function, vm: Component): any {
  try {
    return data.call(vm, vm)
  } catch (e) {
    handleError(e, vm, `data()`)
    return {}
  }
}
  1. this.$data 实际上访问的是this._data
export function stateMixin (Vue: Class<Component>) {
  // flow somehow has problems with directly declared definition object
  // when using Object.defineProperty, so we have to procedurally build up
  // the object here.
  const dataDef = {}
  dataDef.get = function () { return this._data }
  const propsDef = {}
  propsDef.get = function () { return this._props }
  if (process.env.NODE_ENV !== 'production') {
    dataDef.set = function (newData: Object) {
      warn(
        'Avoid replacing instance root $data. ' +
        'Use nested data properties instead.',
        this
      )
    }
    propsDef.set = function () {
      warn(`$props is readonly.`, this)
    }
  }
  Object.defineProperty(Vue.prototype, '$data', dataDef)
  Object.defineProperty(Vue.prototype, '$props', propsDef)
}
  1. this.xxx 实际上访问的是this._data.xxx
function initData (vm: Component) {
  let data = vm.$options.data
  data = vm._data = typeof data === 'function'
    ? getData(data, vm)
    : data || {}

  const keys = Object.keys(data)
  const props = vm.$options.props
  let i = keys.length
  while (i--) {
    const key = keys[i]
    if (props && hasOwn(props, key)) {
      // 如何key在props中则报警告且不处理
      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)) {//key不以$和_开头
      proxy(vm, `_data`, key)
    }
  }
}
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)
  // 假设访问this.name,实际代理如下
  // Object.defineProperty(vm,'name',{
  //   enumerable: true,
  //   configurable: true,
  //   get:function proxyGetter(){return this._data.name},
  //   set:function proxySetter(val){this._data.name=val}
  // });
}
  1. 所以可以通过 Object.assign(this.$data,this.$options.data.call(this)) 重置数据为当前组件的初始数据
  • 事实上访问this.$data就是访问this._data,Object.assign把this.$options.data.call(this)执行的结果对象,依次遍历属性调用function proxySetter(val){this._data.name=val} ,修改了this._data的属性为初始值

下面是一个简化版的测试代码:

let obj={
  _data:{num:3}
}

Object.defineProperty(obj,'num',{
  get:function proxyGetter(){return this._data.num},
  set:function proxySetter(val){this._data.num=val}
})
Object.defineProperty(obj,'$data',{
  get:function () { return this._data }
})
Object.assign(obj.$data,{num:333})
// 相当于Object.assign(obj._data,{num:333})
console.log(obj.num,obj.$data.num) //333 333
发布了38 篇原创文章 · 获赞 66 · 访问量 7万+

猜你喜欢

转载自blog.csdn.net/lyt_angularjs/article/details/105118590