浅谈 vue 中的 watcher
观察Watchers
虽然计算属性在大多数情况下更合适,但有时也需要一个自定义的watcher。这是为什么Vue提供一个更通用的方法通过watch选项,来响应数据的变化。当你想要在数据变化响应时,执行异步操作或开销较大的操作,这是很有用的。
大家对于watch应该不陌生,项目中都用过下面这种写法:
watch:{ someProp(){ //dosomething } } //或者 watch:{ someProp:{ deep:true, handler(){ //dosomething } } }
上面的写法告诉vue,我需要监听someProp属性的变化,于是vue在内部就会为我们创建一个watcher对象。(限于篇幅,我们不聊watcher的具体实现,感兴趣的可以直接看源码watcher)
然而在vue中,watcher的功能并没有这么单一,先上段代码:
a:{{a}}
b:{{b}}
在线demo
上面代码非常简单,我们现在主要关注created钩子中打印的this._watchers,如下:
分别展开三个watcher,观察每一个expression,从上到下分别为:
b(){returnthis.a*2;↵} "a" function(){vm._update(vm._render(),hydrating);↵}
上面三个watcher代表了三种不同功能的watcher,我们将其按功能分为三类:
- 在watch中定义的,用于监听属性变化的watcher(第二个)
- 用于computed属性的watcher(第一个)
- 用于页面更新的watcher(第三个)
normal-watcher
我们在watch中定义的,都属于这种类型,即只要监听的属性改变了,都会触发定义好的回调函数
computed-watcher
每一个computed属性,最后都会生成一个对应的watcher对象,但是这类watcher有个特点,我们拿上面的b举例:
属性b依赖a,当a改变的时候,b并不会立即重新计算,只有之后其他地方需要读取b的时候,它才会真正计算,即具备lazy(懒计算)特性
render-watcher
每一个组件都会有一个render-watcher,function(){↵vm._update(vm._render(),hydrating);↵},当data/computed
中的属性改变的时候,会调用该render-watcher来更新组件的视图
三种watcher的执行顺序
除了功能上的区别,这三种watcher也有固定的执行顺序,分别是:
computed-render->normal-watcher->render-watcher
这样安排是有原因的,这样就能尽可能的保证,在更新组件视图的时候,computed属性已经是最新值了,如果render-watcher排在computed-render前面,就会导致页面更新的时候computed值为旧数据。
下面从一段实例代码中看下vue中的watcher
在这个示例中,使用watch选项允许我们执行异步操作(访问一个API),限制我们执行该操作的频率,并在我们得到最终结果前,设置中间状态。这是计算属性无法做到的。
Askayes/noquestion:
{{answer}}
小结
本文并不是源码解析类文章,只是从一个角度来聊聊,那些看似不相关的东西(computed/watch/页面更新),内部却有着紧密的联系,希望能抛砖引玉,让大家更深入的去探索vue