muwoo/blogs

Vue官网中的约束源码解释 -- 数据与方法

muwoo opened this issue · 0 comments

muwoo commented

当一个 Vue 实例被创建时,它向 Vue 的响应式系统中加入了其 data 对象中能找到的所有的属性。当这些属性的值发生改变时,视图将会产生“响应”,即匹配更新为新的值。

// 我们的数据对象
var data = { a: 1 }

// 该对象被加入到一个 Vue 实例中
var vm = new Vue({
  data: data
})

// 获得这个实例上的属性
// 返回源数据中对应的字段
vm.a == data.a // => true

// 设置属性也会影响到原始数据
vm.a = 2
data.a // => 2

// ……反之亦然
data.a = 3
vm.a // => 3

为什么不是访问vm.$options.data.a而是 vm.a

其实我们知道,为new Vue({data: ...})的时候,会进行mergeOptions,也就是吧所有的参数挂载到vm.$options中,我们定义的data也是会被挂载进去,那么,为什么我们可以通过vm.a来取到我们想要的值呢?我们来看一下源码的实现:

// core/instance/state.js
function initData (vm: Component) {
  let data = vm.$options.data
  data = vm._data = typeof data === 'function'
    ? getData(data, vm)
    : data || {}
   ...
}

Vue通过initData函数,为实例vm定义了一个_data属性,他的值等于我们的vm.$options.data。并做了函数处理,因为有可能我们是通过一个functionreturn一个data。那到这一步,我们顶多可以通过this._data.xx来访问属性,那如何实现this.xx来访问呢?我们接着来看:

...
const keys = Object.keys(data)
let i = keys.length
while (i--) {
  ...
   proxy(vm, `_data`, key)
}
...

我们省略了一些代码,主要来看核心的实现。首先我们会遍历data中定义的属性,然后有一个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)
}

这里用了一个Object.defineProperty函数来定义了target = vmsourceKey = _datakey = xx。并改写了target.keygetset方法。
到这里我们就明白了,当我们访问this.xx的时候,其实是被Object.defineProperty拦截了,代理到this._data.xx上面。