Skip to content
文章目录

依赖收集与派发更新

什么是依赖收集?依赖收集的目的是什么?

依赖收集就是对订阅数据变化的 Watcher 收集的过程。其目的是当响应式数据发生变化,触发它们的 setter 时,能够知道应该通知哪些订阅者去做相应的逻辑处理。例如,当在 template 模板中使用到了某个响应式变量,在组件初次渲染的时候,对这个响应式变量而言,应该收集 render watcher 依赖,当其数据发生变化触发 setter 时,要通知 render watcher 进行组件的重新渲染。

Observe 类:用于将响应式对象的属性转换成可以被检测的属性(为其属性添加 getter 和 setter)

Dep 类:用于收集当前响应式对象的依赖

Watcher 类:作为一个中介(观察者),当数据发生变化时,通过 watcher 中转通知组件。watcher 实例分为渲染 watcher、计算 watcher、侦听器 watcher

在 getter 中收集依赖,在 setter 中出发依赖(diff)

在 getter 中收集依赖,在 setter 中出发依赖(diff)

在 getter 中收集依赖,在 setter 中出发依赖(diff)

在 getter 中收集依赖,在 setter 中出发依赖(diff)

Dep

整个 getter 依赖收集的核心是 Dep,将依赖收集的代码封装成一个 Dep 类,用它来专门管理依赖,每个 Observer 的实例成员中都有一个 Dep 的实例

依赖清空

为什么做 deps 订阅的移除呢?

为了避免这种场景:v-if 已不需要的模板依赖的数据发生变化时就不会通知watcherupdate

当我们根据 v-if 渲染 a 和 b 模版,当条件满足我们渲染 a,会访问 a 中的数据,对 a 的数据添加 getter 进行依赖收集,修改了 a 的数据会通知那些订阅者。当时改变了条件渲染 b 后,我们如果没有进行依赖移除,修改到 a 的模版数据时,又会触发 a 数据的订阅的回调,这显然有浪费的。

派发更新主要做什么事情

派发更新就是当响应式数据发生变动的时候,通知所有订阅了这个数据变化的 Watcher(既 Dep 依赖)执行 update。对于 render watcher 渲染 Watcher 而言,update 就是触发组件重新进行渲染;对于 computed watcher 计算属性 Watcher 而言,update 就是对计算属性重新求值;对于 user watcher 用户自定义 Watcher 而言,update 就是调用用户提供的回调函数。

flushSchedulerQueue:负责刷新 wather 队列,即执行 queue 数组中的每个 watcher 的 run 方法,从而进入更新阶段。比如执行组件更新函数或者执行用户 watcher 的回调函数

vue 做派发更新的一个优化点:不是每次数据改变都出发 watcher 回调,而是把 watcher 添加到一个队列里,然后再 nextTick 后执行 flushSchedulerQueue

图解依赖收集和派发更新

依赖收集

派发更新

queueWatcher:就是将 watcher 去重,然后添加到队列中。然后执行 nextTick(flushSchedulerQueue)异步更新。

flushSchedulerQueue:这个方法简单来说就是对 queue 队列排序,然后遍历该数组,执行 watcher.run()。

watcher.run():这个 run()方法其实最终就是调用了 watcher 的 get 方法,这个 get 我们在前面看过了,最主要的就是去调用了 data 数据的 get 方法,获取最新数据。因此我们可以理解 run()就会进行新一轮的依赖收集,从而获取最新的数据。

总结依赖收集和派发更新

当访问响应式数据时,触发 getter,调用 dep.depend()进行依赖收集到 subs 中,这个过程我们叫依赖收集;而当这些响应式数据发生变化,触发它们的 setter 的时候,能知道应该通知哪些订阅者去做相应的逻辑处理,我们把这个过程叫派发更新。

参考资料

派发更新

依赖收集

vue2.x 源码解读:依赖收集、派发更新

图解:vue 依赖收集及派发更新

Vue 异步更新机制以及$nextTick 原理