关于 dep 的一点疑惑和理解
来源:4-10 检测变化的注意事项

ominus3
2020-05-14
响应式处理的过程中会有两种 dep,大致如下
const dep = new Dep()
const val = obj[key]
let childOb = observe(val)
Object.defineProperty(obj, key, {
get() {
dep.depend()
childOb && childOb.dep.depend()
}
})
昨天晚上仔细思考了一下这里发现比自己想象中的绕。我先简单的把二者区分为闭包 dep(上面 new 出来的 dep) 和 ob 的 dep(通过 observe 生成的可以通过数据本身访问到的 dep)。
我理解 js 中对于对象的访问有两种情况:
- 一种是对象本身被访问,比如下面的 me.person 就是对于 person 对象本身的访问。
- 一种是对象内部的值被访问,比如下面的 me.person.age 就是对内部 age 属性的访问。
因此对象内部的访问也必然触发对象本身的访问。
通过 Object.defineProperty 我们可以拦截到内部的值被访问(新添加的属性除外),但是无法获取到对象本身被访问的情况,假使
new Vue({
data() {
return {
me: {
person: {
age: 22
}
}
}
}
})
模板中访问了 me.person.name,由于 name 并无定义,因此 me.person 对象的闭包 dep (有一个,但属于 age 属性的)没有捕获到这个访问,只有对象 me 的 key 为 person 的闭包 dep 拦截到了这个访问,若后续新增了属性 name,除了 me 的闭包 dep 根本无处通知。
因此 childOb 的作用也就是当模板访问 me.person.name 的时候,在通知 me 的 key 为 person 的闭包 dep 的同时,通知到 person 对象(通过__ob__打在对象上让对象可以自由访问到): person 你在这里被访问了,至于有没有访问内部属性,访问的属性存不存在我不知道、我也不管。你自己记录一下,以备有用。
大部分情况下都是闭包 dep 在发挥作用,直到新增属性以及数组元素变更(数组并没有自己的闭包 dep),需要访问数据属性上的 dep 手动触发通知。
因此:
- 一个响应式的对象拥有两种 dep,一种是可以通过 __ob__.dep 访问到的,一种是和自己的 key 一一对应的闭包 dep,闭包 dep 无法直接访问。
- 这个对象只要被访问,就会触发 __ob__.dep 的依赖,只有访问到相应的 key 才会触发 key 对应的闭包 dep 的依赖,因此可以认为 __ob__.dep 捕获到的 watcher 是内部闭包 dep 捕获到的 watcher 的一个超集?
- 二中提到的两种依赖实际是发生在不同阶段的, __ob__.dep 的依赖 发生在父属性的响应式处理的过程中,闭包 dep 的依赖则发生在对象本身响应式处理的过程中。
希望老师可以帮忙看看后面这三点理解有没有什么问题,先谢过老师!
1回答
-
ustbhuangyi
2020-05-14
有问题,首先我们先明确你说的闭包 dep 和 __ob__.dep
这个是闭包 dep
这个是 __ob__.dep
首先,对象被访问,是会触发 getter 的,
这个 defineReactive 的过程是递归的,比如 data 上的一个深层次对象,都会递归执行 Object.defineProperty,比如你的例子
访问 this.me 的时候会触发 getter,访问 this.me.person,也会触发 getter
而 getter 的内部,是执行的 dep 的 depend,也就是闭包 dep 去收集依赖。
那下面的 childOb.dep.depend() 是干啥的呢。
还是上述的例子,this.me.person 就是 this.me 的 childOb,执行了 childOb.depend() 也就是让 __ob__.dep 去收集依赖,记住这个 __ob__ 是 childOb
我们知道由于没有在 person 下定义 name
直接添加 this.me.person.name 是不会触发响应式更新的,那么可以通过 this.$set API,this.$set API 做了啥呢?
最关键的就是这两行代码,this.$set(this.me.person,'name','zhangsan')
函数里的 ob 就是前面的 childOb,由于之前以及依赖收集了,那么就可以通过 ob.dep.notify() 派发更新了。212020-05-14
相似问题