响应式对象,核心是利用 Object.defineProperty
给数据添加了 getter 和 setter,目的是为了在我们访问数据和写数据时自动执行一些逻辑:getter 用来依赖收集,setter 来派发更新。
The Object.defineProperty
// obj 要在其上定义属性的对象
// prop 定义或修改属性的名称
// descriptor 将被定义或修改的属性描述符
Object.defineProperty(obj, prop, descriptor);
descriptor
是比较核心的,有很多可选值。其中get
、set
是我们最关心的,
get是给obj一个属性提供的getter方法,当我们访问属性时就会触发getter方法,
set是給obj一个属性提供的setter方法,当我们对该属性做修改的时候就会触发setter方法。
一个对象拥有了 getter
、setter
后,我们可以简单地把这个对象称为响应式对象。
initState
对 props、methdos、data、computed 和 watcher 等属性做初始化操作。
- initProps,遍历 props 配置,做两件事:调用 defineReactive 方法,把每个
prop
对应的值变成响应式,可以通过vm._props.xxx
访问到定义在props
中的属性。另一个是通过proxy
把vm._props.xxx
的访问代理到vm.xxx
上。 - initdata,
data
的初始化也是做两件事,对定义data
函数返回对象的遍历,通过proxy
把每个vm._data.xxx
都代理到vm.xxx
上;另一个是调用observe
方法观测整个data
的变化,把data
也变为响应式,可以用过vm._data.xxx
访问到定义在data
返回函数中对应的属性。proxy
代理,将props
和data
上的属性代理到vm
实例上,下面这个例子
为什么我们可以直接使用定义在let comP = {
props: {
msg: 'hello'
},
methods: {
say() {
console.log(this.msg)
}
}
}
props
中的 msg 呢,因为如下 prox 方法做了一些事: ```javascript const sharedPropertyDefinition = { enumerable: true, configurable: true, get: noop, set: noop }
// proxy(vm, _data
, key) 使用方式
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[sourceKey][key]` 的读写变成为 `target[key]` 的读写。所以对于 `vm._props.xxx` 的读写变为 `vm.xxx` 读写,`vm._data.xxx` 同理。
<a name="sNxUU"></a>
## observe
`observe` 的功能是来检测数据变化
```javascript
export function observe (value: any, asRootData: ?boolean): Observer | void {
if (!isObject(value) || value instanceof VNode) {
return
}
let ob: Observer | void
if (hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) {
ob = value.__ob__
} else if (
shouldObserve &&
!isServerRendering() &&
(Array.isArray(value) || isPlainObject(value)) &&
Object.isExtensible(value) &&
!value._isVue
) {
ob = new Observer(value)
}
if (asRootData && ob) {
ob.vmCount++
}
return ob
}
该方法的作用是给非 VNode
的对象类型数据添加一个 Observer
,即实例化一个 Observer
对象实例。
Observer
注意写法上就差一个 r
,Observer
对象是一个类,作用是给对象的属性添加 getter
和 setter
,用于依赖收集和派发更新:
export class Observer {
value: any;
dep: Dep;
vmCount: number; // number of vms that has this object as root $data
constructor (value: any) {
this.value = value
this.dep = new Dep()
this.vmCount = 0
def(value, '__ob__', this)
if (Array.isArray(value)) {
const augment = hasProto
? protoAugment
: copyAugment
augment(value, arrayMethods, arrayKeys)
this.observeArray(value)
} else {
this.walk(value)
}
}
/**
* Walk through each property and convert them into
* getter/setters. This method should only be called when
* value type is Object.
*/
walk (obj: Object) {
const keys = Object.keys(obj)
for (let i = 0; i < keys.length; i++) {
defineReactive(obj, keys[i])
}
}
/**
* Observe a list of Array items.
*/
observeArray (items: Array<any>) {
for (let i = 0, l = items.length; i < l; i++) {
observe(items[i])
}
}
}
defineReactive
/**
* Define a reactive property on an Object.
*/
export function defineReactive (
obj: Object,
key: string,
val: any,
customSetter?: ?Function,
shallow?: boolean
) {
const dep = new Dep()
const property = Object.getOwnPropertyDescriptor(obj, key)
if (property && property.configurable === false) {
return
}
// cater for pre-defined getter/setters
const getter = property && property.get
const setter = property && property.set
if ((!getter || setter) && arguments.length === 2) {
val = obj[key]
}
let childOb = !shallow && observe(val)
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: function reactiveGetter () {
const value = getter ? getter.call(obj) : val
if (Dep.target) {
dep.depend()
if (childOb) {
childOb.dep.depend()
if (Array.isArray(value)) {
dependArray(value)
}
}
}
return value
},
set: function reactiveSetter (newVal) {
const value = getter ? getter.call(obj) : val
/* eslint-disable no-self-compare */
if (newVal === value || (newVal !== newVal && value !== value)) {
return
}
/* eslint-enable no-self-compare */
if (process.env.NODE_ENV !== 'production' && customSetter) {
customSetter()
}
if (setter) {
setter.call(obj, newVal)
} else {
val = newVal
}
childOb = !shallow && observe(newVal)
dep.notify()
}
})
}
Data、Observer、Dep 和 watcher 之间的关系。