vue源码之computed

用了vue这么久,相信大家对computed并不陌生.知道它怎么用,但要说出它的优势以及为什么会有这些优势,相信大多数人都会侃侃而谈:缓存,关联属性变化时重新计算,但是本着实事求是的态度我们将从源码的角度去看看它的优势。首先我们来看看怎么初始化一个computed


图1

我们今天只讨论浏览器端的实现,我们来看看这段代码干了什么,首先创建了一个空的对象赋值给watcher,然后判断是否是服务端渲染,接着遍历我们的computed对象(vue文件中的computed),循环体内首先是获取每个计算属性的value(对应的function)并且进行类型校验,接下来是在非SSR环境创建Watcher对象并赋值给vm._computedWatchers,然后判断当前计算属性的key不在vue实例上执行defineComputed方法,反之则报错。我们接着来看defineComputed方法做了什么


图2

首先判断是否需要缓存,判断依据是非服务端渲染则缓存,接下来判断计算属性value类型,当value是function类型的时候,直接走到createComputedGetter方法并赋值给sharedPropertyDefinition.get,否则的话处理value为对象的情况,这个我们稍后再说。我们回到createComputedGetter方法。


图3

我们看到createComputedGetter返回了一个方法,这个方法会赋值给图2的sharedPropertyDefinition.get,然后图2最后对该计算属性进行劫持。当获取该计算属性的值的时候则会触发createComputedGetter返回的方法。我们看看返回的方法内部做了什么,首先拿到图1记录在vue实例上的_computedWatchers对象,然后获取计算属性绑定的watcher对象,看到这里你可能会奇怪,说好的缓存呢怎么找不到,别着急,我们接着往下看。拿到watcher对象后,当watcher的dirty为真的时候执行watcher的evaluate方法,我们来看看watcher的dirty是个什么东西


图4

我们看到当new Watcher的时候dirty的值取的是options.lazy的值,我们回到图1红线圈起来的地方,可以看到这个值是true。也就是说当计算属性第一次被访问的时候会触发watcher的evaluate方法,我们来看看watcher的evaluate方法都干了什么


图5

evaluate方法首先计算了当前计算属性的值并赋值给了watcher实例的value,然后精髓来了,我们看到dirty的值值被设置为false了,看到这里是不是有种恍然大悟的感觉,下次获取计算属性的值的时候就不会再走这个方法了而是直接从图3 返回watcher.value,这不就是我们要找的缓存吗(终于等到你)

看到这里你或许会问,那他第一次将dirty的值设置为false了,那内部的关联属性更新了怎么重新计算呢,带着这个问题我们接着看,我们知道vue会对data内的属性进行依赖收集(当触发属性的get时),当computed内部引用data中的数据时实际上就是触发了属性的get,这个时候计算属性的watcher会被push到属性的dep,当属性的值修改的时候触发它的set进行变更下发去触发收集到的watcher的update方法


图6

我们看到这里watcher的dirty值又被重置为true了,等下次计算属性被访问的时候又会重新计算。

我们接着回到之前之前遗留的问题,当计算属性的值为对象。当该值是一个对象时如果我们设置cache为false的时候会直接走到createGetterInvoker方法


图7

同样将返回的方法直接赋值给sharedPropertyDefinition.get当该属性被访问的时候我们可以看到直接执行了方法,这样就每次访问都会重新计算。

到此,computed的部分就看完了。

你可能感兴趣的:(vue源码之computed)