肉烂在锅里

个人站

我是软件研发中心培训主管,我喜欢看动漫,学习web前端开发。


依赖收集

Vue是在什么时候进行依赖收集的?

答:

Vue将props、data等数据添加Oberser之后,就有了getter,getter中包含的逻辑就是依赖收集。所以只要当数据的getter得到触发就会触发依赖收集的过程,相反即使添加了响应是对象,如果他的getter没有得到触发也不会触发依赖收集。

Vue在初次渲染DOM的时候,首先需要将render函数转换为VNode,也就是说在执行vm._render函数的时候访问了模板上所‘提到’的变量,因此这些变量的getter也就得到了触发。而那些没有‘提到’的响应式对象并不会进行依赖收集。

Vue这样做也是十分合情合理的,比如props、data中提到的变量没有被当前模板用到,说明他们的改变并不会直接改变dom的更新,那就没有必要立刻进行依赖收集。

Vue是如何进行依赖收集的?

答:

再依赖收集之前,Vue还需要作出一些提前准备,其中包括将:数据变成响应式,为Dep.target添加全局的渲染Watcher(render Watcher)。

数据便响应式我们之前已经提到过了。现在主要来看Vue是如何添加渲染Watcher的。

之前我们讨论过,Vue在执行mountComponent企图去渲染并挂载DOM的时候执行了这么一段核心逻辑:

在这里Vue new了一个Watcher实例,这个Watcer就是render watcher。

Watcher的逻辑判断比较多,方法使用比较多,但是在new Watcher的时候执行了

This.get()方法,进入了get函数。

进入get中,首先执行了pushTarget方法:

pushTarget的方法非常简单,他就是为Dep类的静态属性target赋值为当前带入得到参数watcher,这里带入的是全局的render Watcher,所以Dep.target现在就是这个render Watcher。之所以这样做,是因为Vue希望在同一时间内只有一个全局的唯一Watcher被计算。

pushTarget函数执行完成,回到get(),在get()中执行了this.getter方法,也就是相当于执行了渲染Watcher的回调函数。

执行完pushTarget,Vue在依赖收集前进行的依赖收集准备已经就绪,可以进行依赖手机了,现在执行了回调updateComponet:

刚才提到过_render函数的执行会触发模板上‘提到’的变量的getter。

也就执行了变量的getter逻辑:

在确定Dep.target存在后,首先执行了dep.depend()方法。然后执行了childOb.depand方法,这个地方突然看可能会有点懵,其实只要知道childOb就是当前要defineReactive的数据对象即可,它相当于这个数据对象所持有的dep。如果这个数据不是对象或者数组,那么childOb就是undefined。Vue为什么要这样做?不要忘记,在为这个数据对象oberser(value)的时候,首先判断了他是不是一个非VNode的对象,如果不是的话直接返回undefined。

所以只有非Vnode的对象才有权执行observe去添加Observer,从而为这个非VNode对象添加了一个__ob__属性,然后为这个对象执行defineReactive去添加getter和setter。这个__ob__属性和这个非VNode对象所持有的dep实例虽不是一个实例,但是却‘长’的一样。我想,这也许就是Vue为了将数据对象所持有的dep实例的状态映射到的__ob__.dep上,从而可以在外界查看到这个对象所持有的dep实例的状态。

这事你可能存在疑惑,那非对象和非数组对象该怎么办?难道就不为他们也变成响应式了么?

至于这一点,我们需要回到defineReactive和initState方法中,以data中的数据为例,

initData的最后有一个细节:

可以看到,observe在这里直接将整个data都带入了observe方法中去添加Observer,

而在Observer中Vue为整个data对象添加了__ob__,然后执行了defineReactive方法去为整个data对象进行响应式添加。

然后在definedReactive中:

因为data必定是一个对象,所以最初的observer不会直接return,他的每一个子对象和属性都必然会经过一个defineReactive的过程。这样经过一层层递归,即使是普通的基本类型也会有机会设置getter和setter。并且子对象也会被递归的变化成响应式的。

在得知data中的每个值都会被设置getter和setter后,一旦触发getter就会进行依赖收集的过程。依赖收集就是先执行这个数据对象所持有的dep实例的depend方法:

执行depend方法,就会执行Dep.target.addDep(this)。

Dep.target是当前正在计算的render watcher,因此addDep是Watcher实例的方法:

这个watcher实例,此时也就是render watcher就会把当前的这个数据对象所持有的dep通过塞进自己的newDeps数组中,然后通过调用这个dep的addSubs方法把自己添加到这个dep的subs数组中。这个过程可以视为是当前正在计算的全局唯一watcher和来自各个部门的dep进行名片交换的过程。watcher不仅仅要记录下自己订阅的dep都有谁,也得让dep认识自己。所以在这可以清晰的看到,Vue的订阅者(watcher)和发布者(dep)是多对多的关系。

截至至此,Vue进行依赖收集的过程就算是结束了。

打赏一个呗

取消

感谢您的支持,我会继续努力的!

扫码支持
扫码支持
扫码打赏,你说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦