响应式对象,核心是利用 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` 的功能是来检测数据变化```javascriptexport function observe (value: any, asRootData: ?boolean): Observer | void {if (!isObject(value) || value instanceof VNode) {return}let ob: Observer | voidif (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 $dataconstructor (value: any) {this.value = valuethis.dep = new Dep()this.vmCount = 0def(value, '__ob__', this)if (Array.isArray(value)) {const augment = hasProto? protoAugment: copyAugmentaugment(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/settersconst getter = property && property.getconst setter = property && property.setif ((!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) : valif (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 之间的关系。
