Vue源码学习3-响应式原理-defineReactive
前言
前面简单的梳理了 props data methods 的流程
从这篇文章开始,后面会深入理解Vue的核心之一:响应式原理,这篇文章可以稍微细看
--以下所有代码来自Vue 2.6.11 版本
Object.defineProperty
这个东西有什么用?我想只要是去了解过Vue响应式原理的人都应该知道,我就不多说了,不知道的就自行查阅。
可以在源码的这个目录 src/core/observer/index.js 中找到这段代码(代码略长,建议先跳过),defineReactive 在前面讲 props 的时候就见到过,作用就是将普通数据处理成响应式数据,然后就用到了 Object.defineProperty 这个方法。
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 if (newVal === value || (newVal !== newVal && value !== value)) { return } if (process.env.NODE_ENV !== 'production' && customSetter) { customSetter() } if (getter && !setter) return if (setter) { setter.call(obj, newVal) } else { val = newVal } childOb = !shallow && observe(newVal) dep.notify() } }) }
observe
observe 我们在 initData 方法的最后就有见到过,进到方法中可以看到一下代码
1:判断value是否是 Object 类型,observe只会对引用数据类型(Object或者Array)走后面的逻辑
2:判断value是否有 __ob__属性,如果有,说明它已经被Vue处理成了响应式对象了,无需走后面的逻辑了,这也是为了防止重复处理同一个对象
3:第二个 else if 判断略多,不过正常情况下我们是能够顺利走到 new Observer 逻辑的,Observe 的逻辑进去之后,value就会被打上 __ob__ 标记,即代表它已经是响应式对象了
4:Oberver是个class,定义在 src/core/observer/index.js 里面,它的整个逻辑也很简单(下面贴了段源码),构造函数中有个判断,数组和普通的Object对象各走一段逻辑,这里就看非数组情况下的逻辑(数组的逻辑后面单独拎出来写写就行了)
5:走到walk方法,就是一个遍历,类似于前面讲的 initProps 的逻辑,调用 defineReactive 方法处理对象中的每个字段
这个时候回看上面的 defineReactive 的代码,可以看到其中也调用了 observe 方法,走到这里就能梳理出一个递归处理 data 对象的逻辑
紧接着就是 Object.defineProperty 的逻辑,data或者props 对象中每个字段的值会通过闭包缓存在内存中,并且和一个Dep对象(它的作用后面来说)绑定在一起,当我们通过 this.xxx 获取 data 中的某个字段的时候,就会触发 get 的逻辑,修改则会触发 set 的逻辑 (this.xxx = xxxxx),get 和 set 就是Vue中所谓的依赖收集和派发更新的源头
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 }
export class Observer { value: any;bind dep: Dep; vmCount: number; // number of vms that have 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)) { if (hasProto) { protoAugment(value, arrayMethods) } else { copyAugment(value, arrayMethods, arrayKeys) } this.observeArray(value) } else { this.walk(value) } } walk (obj: Object) { const keys = Object.keys(obj) for (let i = 0; i < keys.length; i++) { defineReactive(obj, keys[i]) } } .....代码省略 }
总结
代码走到这里,梳理出了一个递归处理 data 和 props 对象的逻辑,也知道了 defineReactive 方法的作用。
其中有两个class,一个Oberver 上面简单说了一下,还有一个 Dep 以及一个至此还未见到的重要的 class --Watcher,这两个东西是整个响应式原理的最重要的两个class,它们和两种设计模式密切相关 观察者模式 和 订阅发布模式,这两个东西网上已经有很多人说过了,这里就不多扯了,不懂就自行查阅
发表回复