根据情况对数据进行遍历添加 __ob__ ,将 Dep.target 收集到 childOb.dep 的观察者列表,以便在增加/删除数据时能通知到观察者
通过 dependArray 将数组型的 value 递归进行观察者收集,在数组元素发生增、删、改时能通知到观察者
target[key] 的 setter 主要作用是对新数据进行观察,并通过闭包保存到 childOb 变量供 getter 使用,同时调用 dep.notify 通知观察者,在此就不再展开。
Watcher
在前面的篇幅中,我们主要介绍了 defineReactive 来定义响应式数据:通过闭包保存 dep 和 childOb ,在 getter 时来进行观察者的收集,使得在数据修改时能触发 dep.notify 或 childOb.dep.notify 来调用观察者的方法进行更新。但具体是如何进行 watcher 收集的却未做过多解释,现在我们将通过阅读 Watcher 来了解观察者背后的逻辑。
function initComputed (vm: Component, computed: Object) { const watchers = vm._computedWatchers = Object.create(null) const isSSR = isServerRendering() for (const key in computed) { const userDef = computed[key] const getter = typeof userDef === 'function' ? userDef : userDef.get if (!isSSR) { // create internal watcher for the computed property. watchers[key] = new Watcher( vm, getter || noop, noop, computedWatcherOptions ) } ... } }
这是 Vue 计算属性的初始化操作,去掉了一部分不影响的代码。首先初始化对象 vm._computedWatchers 用以存储所有的计算属性, isSSR 用以判断是否为服务端渲染。再根据我们编写的 computed 键值对循环遍历,如果不是服务端渲染,则为每个计算属性实例化一个 Watcher ,并以键值对的形式保存到 vm._computedWatchers 对象,接下来我们主要看下 Watcher 这个类。
Watcher 的构造函数
构造函数接受5个参数,其中当前 Vue 实例 vm 、求值表达式 expOrFn (支持 Function 或者 String ,计算属性中一般为 Function ),回调函数 cb 这三个为必传参数。设置 this.vm = vm 用以后续绑定 this.getter 的执行环境,并将 this 推入 vm._watchers ( vm._watchers 用以维护实例 vm 中所有的观察者),另外根据是否为渲染观察者来赋值 vm._watcher = this (常用的 render 即为渲染观察者)。接着根据 options 进行一系列的初始化操作。其中有几个属性:
- this.lazy:设置是否懒求值,这样能保证有多个被观察者发生变化时,能只调用求值一次。
- this.dirty:配合this.lazy,用以标记当前观察者是否需要重新求值。
- this.deps、this.newDeps、this.depIds、this.newDepIds:用以维护被观察对象的列表。
- this.getter:求值函数。
- this.value:求值函数返回的值,即为计算属性中的值。