深入浅出Vue.js读后总结-watch与computed

546 阅读4分钟

一、watch的用法和内部原理

用法:

vm.$watch(expOfFn,callback,{options})

options中包括deep,immediate

deep:可以发现对象内部的变化,指定deep:true使用

immediate:立即以当表达式当前值触发回调,指定immediate:true使用

内部原理:

vm.$watch是对Watcher的一种封装,Watcher之前也讲过,就是一个中介,数据发生变化通知Watcher,Watcher调用update执行回调。

主要功能为 const watcher = new Watcher(vm,expOfFn,cb,options)

(1)在new Watcher中,先判断是否使用了imediate参数,使用了则立即执行cb

(2)然后判断是否使用了deep参数,如果是,则使用traverse进行深度观察

(3)判断Watcher是否已经订阅了该Dep,防止重复订阅,如果没有订阅的话,Watcher通过this.deps.push(dep)记录自己被哪些Dep收集了

(4)触发dep.addSub(this)来将自己订阅到Dep中

所以Watcher和Dep是多对多的关系(Dep记录了自己要通知哪些Watcher,Watcher也记录了自己要被哪些Dep通知)

Watcher中expOrFn有个细节:Watcher中的expOrFn是支持函数的(看英文也知道)

 //如果是函数,则直接赋值给getter
 if (typeof expOrFn === 'function') {
     this.getter = expOrFn
 }
 //如果是表达式,则用parsePath解析数据
 else {
     this.getter = parsePath(expOrFn)
 }

当expOfFn是函数时,它不仅可以动态的返回数据,而且函数中所有的响应式数据也会被Watcher观察(exOfFn支持函数其实与Computed的实现原理有很大关系)

例如:

    //这里就向watch中传入了函数(也就是expOfFn为函数)
    this.$watch(function(){
        //这时候watcher就会观察name和age两个数据,其中一个有变化都会得到通知,触发回调
        return this.name + this.age
    },function(newValue,oldValue){
        console.log(newValue,oldValue)    
    })

二、computed内部原理

computed是定义在vm上一个特殊的getter方法,模板读取计算属性其实就是触发计算属性的getter方法,这个getter方法用于计算当前计算属性的值,然后会用Watcher取观察计算属性中用到的数据的变化,同时将计算属性的Watcher的dirty属性改为false,这样再次读取计算属性时,如果dirty还为false就不再重新计算.

1.计算属性结果会缓存:

计算属性的结果会被缓存,且只有在计算属性所依赖的响应式属性发生变化后才会重新计算,是通过Watcher的dirty属性来分辨的,为true则要重新计算,为false则不要重新计算。

2.计算属性状态变化流程:

(1)当计算属性中状态发生变化时,计算属性的Watcher和组件的Watcher都会得到通知

(2)计算属性的Watcher会将自己的dirty值设为true,当下一次读取计算属性时,会重新计算一次值

(3)组件的Watcher也会得到通知,执行render进行重新渲染,由于重新渲染也会读取到计算属性的值,然后这个时候经过(2)步骤,计算属性Watcher的dirty值已经为true,所以就会重新计算一次计算属性的值,用于本次渲染。

在vue2.5.17版本中又对计算属性的实现做了一个改动

在此之前,计算属性存在着一个问题:

组件Watcher观察计算属性所有依赖数据的变化,如果计算属性中的状态产生了变化,但是计算属性最终的值并没有改变,这时候计算属性也会重新渲染,只不过虚拟DOM的diff中没有变化,视觉上没用变化而已,但是走了渲染流程。

有人就在Vue的GitHub Issues里面提出了这个问题,为了解决这个问题,作者对计算属性的实现做了一些改动:

组件的Watcher不再观察计算属性用到的数据的变化,而是让计算属性的Watcher得到通知后,取计算一次计算属性的值,和之前的值做比较,如果不一样再去主动通知组件Watcher进行渲染操作。组件的Watcher不再观察数据的变化,而只观察计算属性的Watcher,然后计算属性主动去通知组件。发送通知就是执行getAndInvoke函数的回调。

三、watch和computed对比

(1)watch不支持缓存,数据变化执行回调;computed支持缓存,数据变化则重新计算

(2)computed不支持异步,watch支持异步监听

(3)computed依赖的数据和watch监听的数据都是data声明过,或者props中的数据