🧼

Computed的实现

💡
计算属性实际上也是一个effect,不过进行了一些特殊封装,他的特点就是可缓存
import { isFunction } from '@vue/shared' import { activeEffect, ReactiveEffect, trackEffects, triggerEffects } from './effect' export function computed(getterOrOptions) { let isGetter = isFunction(getterOrOptions) let getter, setter const fn = () => console.warn('computed is readonly') if (isGetter) { getter = getterOrOptions setter = fn } else { getter = getterOrOptions.get setter = getterOrOptions.set || fn } return new ComputedRefImpl(getter, setter) } class ComputedRefImpl { private _value private _dirty = true public effect public deps private __v_isRef = true constructor(getter, public setter) { // new ReactiveEffect的时候会执行getter,会让getter对应的依赖项去收集当前的计算属性的effect this.effect = new ReactiveEffect(getter, () => { // 调度器是在该computed里面的依赖项发生变化的时候触发的(会重新走到set) if (!this._dirty) { this._dirty = true // 当有值修改时去触发对应的effect,渲染页面 triggerEffects(this.deps) } }) // 拿到effect实例让函数执行 } get value() { if (activeEffect) { // 让计算属性去做依赖收集当前的渲染effect trackEffects(this.deps || (this.deps = new Set())) } if (this._dirty) { // 只有当dirty为真才会执行 当计算属性的值发生变化时会调用上面的调度器,并且把dirty打开,然后重新执行getter获取新的值 this._dirty = false this._value = this.effect.run() // 当run的时候会让里面的属性effect把计算属性的effect给收集,下次触发的时候可以去执行计算属性的effect } return this._value } set value(newValues) { this.setter(newValues) } }
  1. 当去新建一个computed的时候,他会去将(getter,setter)参数进行统一,然后返回一个 ComputedRefImpl的实例
  1. ComputedRefImpl的实例上_value表示他的计算属性的值,还有一个_dirty用来处理缓存,他在new的时候会去生成一个Effect,并且把他的getter传进第一个参数,并且添加上调度器的函数,
  1. 当computed的实例被get的时候,会去触发获取值的操作,会在他的deps属性上收集activeEffect
  1. 这时候会判断dirty是否为true,如果为true,就去执行之前的new的effect实例上的run方法 然后把dirty改成false,这样就相当于缓存了这个值
  1. 因为去执行了computed的回调函数,所以这时候,会让回调函数里面的reactive的对象,去收集计算属性的effect,这样当里面的值更改的时候会依次触发effect的时候,会把计算属性的effect也给触发了
  1. 当触发了收集了计算属性effect的属性时,会去触发计算属性effect的调度器,这个调度器会去重新把dirty给打开,等下次用到计算属性的时候,会重新跑一次那个run方法,重新计算出新值
  1. 因为之前new ReactiveEffect的时候传入了一个调度器,这时候,调度器会把计算属性effect之前收集的activeEffect依次执行
 
 

图示

notion image
notion image