vue响应式系统之observe、watcher、dep的源码解析(2)

Watcher类的实现比较复杂,因为他的实例分为渲染 watcher(render-watcher)、计算属性 watcher(computed-watcher)、侦听器 watcher(normal-watcher)三种,
这三个实例分别是在三个函数中构建的:mountComponent 、initComputed和Vue.prototype.$watch。

normal-watcher:我们在组件钩子函数watch 中定义的,都属于这种类型,即只要监听的属性改变了,都会触发定义好的回调函数,这类watch的expression是我们写的回调函数的字符串形式。

computed-watcher:我们在组件钩子函数computed中定义的,都属于这种类型,每一个 computed 属性,最后都会生成一个对应的 watcher 对象,但是这类 watcher 有个特点:当计算属性依赖于其他数据时,属性并不会立即重新计算,只有之后其他地方需要读取属性的时候,它才会真正计算,即具备 lazy(懒计算)特性。这类watch的expression是计算属性中的属性名。

render-watcher:每一个组件都会有一个 render-watcher, 当 data/computed 中的属性改变的时候,会调用该 render-watcher 来更新组件的视图。这类watch的expression是 function () {vm._update(vm._render(), hydrating);}。

除了功能上的区别,这三种 watcher 也有固定的执行顺序,分别是:computed-render -> normal-watcher -> render-watcher。

这样安排是有原因的,这样就能尽可能的保证,在更新组件视图的时候,computed 属性已经是最新值了,如果 render-watcher 排在 computed-render 前面,就会导致页面更新的时候 computed 值为旧数据。

这里我们只看其中一部分代码

function Watcher(vm, expOrFn, cb, options, isRenderWatcher) { this.vm = vm; if(isRenderWatcher) { vm._watcher = this; } vm._watchers.push(this); // options if(options) { this.deep = !!options.deep; //是否启用深度监听 this.user = !!options.user; //主要用于错误处理,侦听器 watcher的 user为true,其他基本为false this.lazy = !!options.lazy; //惰性求职,当属于计算属性watcher时为true this.sync = !!options.sync; //标记为同步计算,三大类型暂无 } else { this.deep = this.user = this.lazy = this.sync = false; } //初始化各种属性和option //观察者的回调 //除了侦听器 watcher外,其他大多为空函数 this.cb = cb; this.id = ++uid$1; // uid for batching this.active = true; this.dirty = this.lazy; // for lazy watchers this.deps = []; this.newDeps = []; this.depIds = new _Set(); this.newDepIds = new _Set(); this.expression = expOrFn.toString(); // 解析expOrFn,赋值给this.getter // 当是渲染watcher时,expOrFn是updateComponent,即重新渲染执行render(_update) // 当是计算watcher时,expOrFn是计算属性的计算方法 // 当是侦听器watcher时,expOrFn是watch属性的名字,this.cb就是watch的handler属性 //对于渲染watcher和计算watcher来说,expOrFn的值是一个函数,可以直接设置getter //对于侦听器watcher来说,expOrFn是watch属性的名字,会使用parsePath函数解析路径,获取组件上该属性的值(运行getter) //依赖(订阅目标)更新,执行update,会进行取值操作,运行watcher.getter,也就是expOrFn函数 if(typeof expOrFn === 'function') { this.getter = expOrFn; } else { this.getter = parsePath(expOrFn); } this.value = this.lazy ? undefined : this.get(); }; //取值操作 Watcher.prototype.get = function get() { //Dep.target设置为该观察者 pushTarget(this); var vm = this.vm; //取值 var value = this.getter.call(vm, vm); //移除该观察者 popTarget(); return value }; Watcher.prototype.addDep = function addDep(dep) { var id = dep.id; if(!this.newDepIds.has(id)) { //为观察者的deps添加依赖dep this.newDepIds.add(id); this.newDeps.push(dep); if(!this.depIds.has(id)) { //为dep添加该观察者 dep.addSub(this); } } }; //当一个依赖改变的时候,通知它update Watcher.prototype.update = function update() { //三种watcher,只有计算属性 watcher的lazy设置了true,表示启用惰性求值 if(this.lazy) { this.dirty = true; } else if(this.sync) { //标记为同步计算的直接运行run,三大类型暂无,所以基本会走下面的queueWatcher this.run(); } else { //将watcher推入观察者队列中,下一个tick时调用。 //也就是数据变化不是立即就去更新的,而是异步批量去更新的 queueWatcher(this); } }; //update执行后,运行回调cb Watcher.prototype.run = function run() { if(this.active) { var value = this.get(); if( value !== this.value || isObject(value) || this.deep ) { var oldValue = this.value; this.value = value; //运行 cb 函数,这个函数就是之前传入的watch中的handler回调函数 if(this.user) { try { this.cb.call(this.vm, value, oldValue); } catch(e) { handleError(e, this.vm, ("callback for watcher \"" + (this.expression) + "\"")); } } else { this.cb.call(this.vm, value, oldValue); } } } }; //对于计算属性,当取值计算属性时,发现计算属性的watcher的dirty是true //说明数据不是最新的了,需要重新计算,这里就是重新计算计算属性的值。 Watcher.prototype.evaluate = function evaluate() { this.value = this.get(); this.dirty = false; }; //收集依赖 Watcher.prototype.depend = function depend() { var this$1 = this; var i = this.deps.length; while(i--) { this$1.deps[i].depend(); } };

总结

Observe是对数据进行监听,Dep是一个订阅器,每一个被监听的数据都有一个Dep实例,Dep实例里面存放了N多个订阅者(观察者)对象watcher。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:http://www.heiqu.com/ae605a9102256cddd3be4c17d4947e02.html