muwoo / blogs

📚一个前端的博客。
2.32k stars 351 forks source link

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

Open muwoo opened 6 years ago

muwoo commented 6 years ago

当一个 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```来取到我们想要的值呢?我们来看一下源码的实现:
```js
// 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上面。