前言
在Observer源码部分主要有三个角色:Observer、Dep、Watcher。
Observer
看这一部分,可以带着以下几个问题来看:
1、满足什么条件可以将其变成响应式的
2、Observer是如何去分别处理传入的数组或者对象的?
3、有两处new Dep,作用分别是什么?
4、核心代码defineReactive干了些什么?
5、Dep.target是什么?defineReactive中get时,为什么要判断Dep.target?
- 满足什么条件可以将其变成响应式的
function observe (value, asRootData) { if (!isObject(value) || value instanceof VNode) { return } let ob 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}复制代码
其实上述代码中有很多判断,我们可以得出以下结论:
(1)必须是一个对象,且不能是vnode的类型的。
- Observer是如何去分别处理传入的数组或者对象的
if (Array.isArray(value)) { if (hasProto) { protoAugment(value, arrayMethods) } else { copyAugment(value, arrayMethods, arrayKeys) } this.observeArray(value)} else { this.walk(value)}复制代码
这段代码就是判断传入的值是不是数组,如果是数组,走observeArray方法,如果不是数组,那么走walk方法。
walk (obj) { const keys = Object.keys(obj) for (let i = 0; i < keys.length; i++) { defineReactive(obj, keys[i]) }}observeArray (items) { for (let i = 0, l = items.length; i < l; i++) { observe(items[i]) }}复制代码
walk这个方法就是遍历对象,然后给对象中的属性值变成响应式的,遍历完之后,整个对象就是响应式对象了。 observeArray这个方法是遍历数组,然后对数组中每一个元素在走一遍响应式流程。
- 有两处new Dep,作用分别是什么
第一处:
class Observer { constructor (value) { 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) } }复制代码
第二处:
function defineReactive ( obj, key, val, customSetter, shallow) { const dep = new Dep() // 省略中间代码 Object.defineProperty(obj, key, { enumerable: true, configurable: true, get: function reactiveGetter () { const value = getter ? getter.call(obj) : val if (Dep.target) { dep.depend() // 省略中间代码 } return value }, set: function reactiveSetter (newVal) { // 省略中间代码 dep.notify() } })}复制代码
第二处dep我们很好理解,是给get和set服务的,进行依赖收集和派发更新。
第一处dep,我们可以考虑一下,他是整个对象的一个属性,那么他何时进行依赖收集和派发更新。
我们可以全局搜一下dep.depend。发现会有三处。有两处是对ob属性进行操作的,也就是对整个对象进行依赖收集。
在全局搜一下dep.notify。发现有四处。有三处是对ob属性进行操作的。分别是set和del,数组的一个方法。
- 核心代码defineReactive干了些什么
这一部分代码可以分为三部分看:定义一些变量、Object.defineProperty、对childOb操作。
// 生成一个新的dep。 const dep = new Dep() // 判断这个对象这个属性是否可以修改 const property = Object.getOwnPropertyDescriptor(obj, key) if (property && property.configurable === false) { return } // 定义getter和setter方法 const getter = property && property.get const setter = property && property.set if ((!getter || setter) && arguments.length === 2) { val = obj[key] }复制代码
get: function reactiveGetter () { // 获取值 const value = getter ? getter.call(obj) : val // 当存在Dep.target的时候进行依赖收集 if (Dep.target) { dep.depend() // 省略中间代码 } // 返回获取到的值 return value},set: function reactiveSetter (newVal) { // 获取原来的值 const value = getter ? getter.call(obj) : val // 将新的值和原值进行对比,如果没有发生改变,就直接返回 if (newVal === value || (newVal !== newVal && value !== value)) { return } // 该属性不能set的情况也直接返回 if (getter && !setter) return // 给该属性赋值 if (setter) { setter.call(obj, newVal) } else { val = newVal } // 重新对这个值进行监听 childOb = !shallow && observe(newVal) // 更新dep中的watcher dep.notify()}复制代码
// 尝试将值转化成响应式对象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) { // 省略中间代码 //因为值发生了变动,所以再一次尝试将其变成一个响应式对象 childOb = !shallow && observe(newVal) } })复制代码
- Dep.target是什么 其实Dep.target是一个全局变量,更是一个wathcer。在dep文件中的源码如下:
Dep.target = nullconst targetStack = []export function pushTarget (target: ?Watcher) { targetStack.push(target) Dep.target = target}export function popTarget () { targetStack.pop() Dep.target = targetStack[targetStack.length - 1]}复制代码
- defineReactive中get时,为什么要判断Dep.target
因为所有定义在data中的值,都会被变成响应式对象,但是每一个不一定有watcher。watcher分为三种:render中生成的watcher、用户自定义的watcher、computed。
上述流程中,当生成一个新的renderWatcher的时候,便会走get流程,然后进行依赖收集,如果没有Dep.target,说明这个值并没有对应的watcher,所以不需要进行依赖收集。 当更新的是时候,又回进行一次依赖收集。