请稍等 ...
×

采纳答案成功!

向帮助你的同学说点啥吧!感谢那些助人为乐的人

关于 dep 的一点疑惑和理解

响应式处理的过程中会有两种 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 中对于对象的访问有两种情况:

  1. 一种是对象本身被访问,比如下面的 me.person 就是对于 person 对象本身的访问。
  2. 一种是对象内部的值被访问,比如下面的 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 手动触发通知。
因此:

  1. 一个响应式的对象拥有两种 dep,一种是可以通过 __ob__.dep 访问到的,一种是和自己的 key 一一对应的闭包 dep,闭包 dep 无法直接访问。
  2. 这个对象只要被访问,就会触发 __ob__.dep 的依赖,只有访问到相应的 key 才会触发 key 对应的闭包 dep 的依赖,因此可以认为 __ob__.dep 捕获到的 watcher 是内部闭包 dep 捕获到的 watcher 的一个超集?
  3. 二中提到的两种依赖实际是发生在不同阶段的, __ob__.dep 的依赖 发生在父属性的响应式处理的过程中,闭包 dep 的依赖则发生在对象本身响应式处理的过程中。

希望老师可以帮忙看看后面这三点理解有没有什么问题,先谢过老师!

正在回答 回答被采纳积分+3

1回答

ustbhuangyi 2020-05-14 13:20:26

有问题,首先我们先明确你说的闭包 dep 和 __ob__.dep
//img1.sycdn.imooc.com//szimg/5ebcd20609e8248812820758.jpg
这个是闭包 dep

//img1.sycdn.imooc.com/szimg/5ebcd23209c07b7714421578.jpg
这个是 __ob__.dep

首先,对象被访问,是会触发 getter 的,//img1.sycdn.imooc.com/szimg/5ebcd26709efb3b215521144.jpg

这个 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 做了啥呢?

//img1.sycdn.imooc.com/szimg/5ebcd431097a807220861196.jpg
最关键的就是这两行代码,this.$set(this.me.person,'name','zhangsan')
函数里的 ob 就是前面的 childOb,由于之前以及依赖收集了,那么就可以通过 ob.dep.notify() 派发更新了。

2 回复 有任何疑惑可以回复我~
  • 提问者 ominus3 #1
    谢谢老师,你讲的这个流程我清楚的。
    
    this.me 的这个(对于 key 为 person ) childOb 也就是 this.me.person.__ob__ 可以访问的。在访问 this.me.person.name 或者 this.me.person.age 的时候都会触发这个 childOb 的 depend,但是只有this.me.person.age 才会触发 this.me.person 内部的一个闭包 dep,因为 name 初始的时候没有定义在 person 上,也无从谈自己的闭包 dep了。
    
    因此我想把 childOb 看作是对对象访问(this.me.person)的拦截,闭包 dep 是对对象内部访问(this.me.person.age)的拦截,触发后者的时候一定会触发前者。
    
    老师觉得有问题可能是我这里为了便于理解,把 this.me.person 认为是对 person 进行了整体的访问,而实际上这里只能算是对 thie.me 的对象的访问。
    回复 有任何疑惑可以回复我~ 2020-05-14 15:10:44
问题已解决,确定采纳
还有疑问,暂不采纳
意见反馈 帮助中心 APP下载
官方微信