「这是我参与2022首次更文挑战的第6天,活动详情查看:2022首次更文挑战」
问题
为什么要再defineReactive创建dep,且new Observer的时候还要创建dep?
源码
vue源码如下
// src/core/observer/index.js 39行
export class Observer {
value: any;
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)) {
...
} else {
...
}
}
}
// src/core/observer/index.js 132行
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
}
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) {
...
}
})
}
现象
能看到Observer里执行了this.dep = new Dep(),创建了dep实例,并挂载在Observer的实例上
然后defineReactive里又执行了const dep = new Dep(),保存在了闭包里
为什么?
思路
data(){
return {
obj:{
foo:{
a:1
},
bar:{
b:2
}
}
}
}
我们以上述数据为例
- defineReactive里的dep只能存储读取当前对象某个属性的watcher,它以对象的key为维度,这个key的值变化了,那就通知这些watcher去update
- 执行Vue.set(foo,'b',100)的时候,defineReactive里面的defineProperty给foo创建完属性,这个属性之前并不存在,所以并有对他做依赖收集,所以没办法通知watcher变更,因为不知道通知谁
- 那么我们分析,给foo增加属性b,那么本质上是foo变更了(这里注意不是b变更了,b之前都没有,谈不上变更),我们应该通知foo对应的watcher去更新,那我们就得想办法获取到foo对应的watcher
- 当我们修改obj的foo属性的时候,如obj.foo = {e:99},其实就是foo发生变更了,那么按上面分析,我们是不是就可以给这时候收集到的对应的watcher存下来,后序给Vue.set(foo,b,1)使用呢?那存在哪呢? --可以存在foo对应的__ob__.dep里。
- 这样我们在set的时候,就可以去取出foo的__ob__.dep来通知更新了
那这延伸出一个问题,foo必须是响应式数据,它才有__ob__,那也就意味着Vue.set(obj)传入的obj必须是响应式数据
复盘
data(){
return {
obj:{
foo:{
a:1
},
bar:{
b:2
}
}
}
}
那么我们再来复盘一下读取和新增属性的流程
-
读取obj.foo => 触发get依赖收集,存一份在闭包,另外存一份在foo.__ob__.dep里
-
obj.foo的值发生变更时,会去通知视图更新,这个watcher对应的是obj.foo属性的变更
-
那么当我们Vue(foo,'c',100)时,其实也是foo发生变更了,我们就可以去foo.__ob__.dep里面取出刚才读取obj.foo的watcher,去通知更新
总结
这里其实很绕,可能看一遍不能理清楚,有时觉得自己清楚了,过一会看有理不清楚了。
我们是这么理解的并抓住重点的
-
修改谁,就是谁变更了,如obj.foo => foo变更
-
给谁新增属性,也是谁变更了,如Vue.set(foo,'c',100) => foo变更
-
set触发不了c的收集,因为之前没有c,所以不做处理的话,后序修改c,无法通知
-
obj.foo读取foo属性的时候保存依赖watcher,一份给foo修改使用存在闭包,一份给foo新增使用,存在foo的响应式数据内