Advanced-Frontend/Daily-Interview-Question

第 141 题:Vue 中的 computed 是如何实现的

yygmind opened this issue · 10 comments

第 141 题:Vue 中的 computed 是如何实现的
HCLQ commented

实质是一个惰性的watcher,在取值操作时根据自身标记 dirty属性返回上一次计算结果/重新计算值
在创建时就进行一次取值操作,收集依赖变动的对象/属性(将自身压入dep中)
在依赖的对象/属性变动时,仅将自身标记dirty致为true

computed本身是通过代理的方式代理到组件实例上的,所以读取计算属性的时候,执行的是一个内部的getter,而不是用户定义的方法。

computed内部实现了一个惰性的watcher,在实例化的时候不会去求值,其内部通过dirty属性标记计算属性是否需要重新求值。当computed依赖的任一状态(不一定是return中的)发生变化,都会通知这个惰性watcher,让它把dirty属性设置为true。所以,当再次读取这个计算属性的时候,就会重新去求值。

反对楼上说的,惰性watcher/计算属性在创建时是不会去求值的,是在使用的时候去求值的。

HCLQ commented

computed本身是通过代理的方式代理到组件实例上的,所以读取计算属性的时候,执行的是一个内部的getter,而不是用户定义的方法。

computed内部实现了一个惰性的watcher,在实例化的时候不会去求值,其内部通过dirty属性标记计算属性是否需要重新求值。当computed依赖的任一状态(不一定是return中的)发生变化,都会通知这个惰性watcher,让它把dirty属性设置为true。所以,当再次读取这个计算属性的时候,就会重新去求值。

反对楼上说的,惰性watcher/计算属性在创建时是不会去求值的,是在使用的时候去求值的。

确实,更正一下

https://ustbhuangyi.github.io/vue-analysis/reactive/computed-watcher.html#computed

链接挂了

这本电子书改版了,所以之前的链接失效了,回到首页就可以看到了

computed中的属性,通过代理,代理到vue的data上,对data进行处理的时候,已经添加了getter方法,然后当从vue实例上取该值的时候,将实例本身传递进computed函数内部,然后就可以拿到返回值了。
所以函数内部有值改变的时候,触发watcher,computed也会跟着重新计算

Wowoy commented

在initComputed中遍历每一个computed属性,创建对应的Watcher。在Watcher实例化过程中,计算computed属性结果,会对依赖的data进行取值,从而触发data的getter进行依赖收集,将当前Watcher加入到订阅者数组中。当computed属性依赖的data改变时,会触发data的setter通知订阅者更新,这个computed会重新计算。

m7yue commented

初始化阶段:
创建每一个计算属性对应的 watcher , dirty 为true, 将计算属性定义到实例上, 就可通过this.xxx 获取。
同时会对计算方法做一层包装:根据 dirty 判断是执行方法还是返回缓存值 (watcher.value)

第一次获取计算属性值:

  1. 会执行 watcher 的 evaluate 方法 , 这个 evalute 就是执行了计算方法 然后更新 watcher 的 value。并且设置了dirty 为 false
  2. 重点: 在执行计算方法的时候, 会触发每一个依赖的响应数据的 get 而此时 Dep.target 就是当前计算属性对应的watcher
  3.   依赖的相应数据 会 将当前的watcher 放入自己管理的订阅器中(依赖收集)。
  4. 这样当某个依赖的相应数据变更的时候,触发订阅器发布更新,会执行上面watcher 的 update方法,这个update方法知道这是个计算属性的watcher 就干了一件事, 把dirty 设为 true

第二次获取计算属性值:
  如果依赖的相应数据又变更,那么 watcher.dirty=true,会执行计算方法获取新值,否则获取到缓存的 watcher.value。

所以computed的缓存是什么原因呢