computed
计算属性的初始化是发生在 Vue 实例初始化阶段的 initState 函数中 initComputed(vm, opts.computed);
1. 创建 vm._computedWatchers 为⼀个空对象
2. 遍历 computed,拿到计算属 性的每⼀个 userDef , 获取这个 userDef 对应的 getter 函数;
3. 为每⼀个 getter 创建⼀个 watcher ,这个 watcher 和渲染 watcher 不同,它是⼀个 computed watcher;
4. 最后对判断如果 key 不是 vm 的属性,则调用 defineComputed(vm, key, userDef) ,否则判断计算属性对于的 key 是否 已经被 data 或者 prop 所占用,如果是的话则在开发环境报相应的警告。
其中 const computedWatcherOptions = { lazy: true }
computedconst sharedPropertyDefinition = {
enumerable: true,
configurable: true,
get: noop,
set: noop
}
defineComputeddefineComputed 就是利用 Object.defineProperty 给计算属性对应的 key 值添加 getter 和 setter。
createComputedGetterr 返回⼀个函数 computedGetter ,它就是计算属性对应的 getter。
createComputedGetter缓存: 在new Watcher时传入的第四个参数 computedWatcherOptions 的 lazy = true,对应就是watcher的构造函数中的 dirty 为true。在 computedGetter 中,如果 dirty 为 false(即依赖的值没有发生变化),就不会重新求值,相当于 computed 被缓存了。
watcher计算属性的 watcher 和 普通 watcher 的不同
在计算属性的 watcher 中, 传入的参数 computedWatcherOptions = { lazy: true }, 所以它的 value = undefined,也不会调用 this.get() 方法
1. 当我们在 首次渲染 render 的时候,访问到我们的计算属性的时候,就会触发 计算属性的 getter,就是 createComputedGetterr,然后会拿到 计算属性的 watcher = this._computedWatchers && this._computedWatchers[key];
如果 dirty 为 true ,就是要重新计算 watcher 的值,如果为false,那么就不用,就是使用缓存。
if (watcher.dirty) {
watcher.evaluate();
}
evaluate2. evaluate 就会调用 get 方法,紧接着调用 this.getter.call(vm, vm), 也就是我们的 computed 的表达式,比如
name(){ return this.count + 1 };
3. 紧接着就会获取 this.count 的 值,也就是调用 count 的 getter 方法,并把 computed watcher 添加到 subs 中, 最后 return value 拿到计算属性的值, 然后 popTatget, 退出当前的 computed watcher。
4. if (Dep.target) { watcher.depend() }
evaluate 执行完毕,如果Dep.target 为 true, 那么 此时的 Dep.target 为 渲染 Watcher,执行 depend。
5 . 现在 count 收集了computed watcher 和 渲染 watcher, 当我们的依赖的数据 count 发生变化的时候,会触发 setter,就会 notify() 所有的 watcher,也包括我们的 computed watcher ,执行 update()。
update当依赖的值发生变化 update 时候,将 dirty = true ,那么就会 watcher.evaluate() 重新计算。
watcher
initWatchinitWatch 对 watch 对象做遍历,拿到每⼀个 handler ,Vue 是支持 watch 的同⼀个 key 对应多个 handler ,所以如果 handler 是⼀个数组,则遍历这个数组,调用 createWatcher ⽅ 法,否则直接调用 createWatcher。
createWatcher 拿到它最终的回调函数,最后调用 vm.$watch(keyOrFn, handler, options) 函数
执行 var watcher = new Watcher(vm, expOrFn, cb, options), 实例化了⼀个 watcher,这是⼀个 user watcher ,因为 options.user = true。
通过实例化 watcher 的方式,⼀旦我们 watch 的数据发送变化,它最终会执行 watcher 的 run 方法,执行回调函数 cb ,并且如果我们设置了 immediate 为 true,则直接会执行回调函数 cb 。最后返回了⼀个 unwatchFn 方法,它会调用 teardown 方法去移除这个 watcher 。
网友评论