前言
在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 = null
const 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,所以不需要进行依赖收集。 当更新的是时候,又回进行一次依赖收集。