vue3-13-剖析vue3 computed 源代码 v2

75 阅读3分钟

computed

let count = ref(0);

let getCount = computed(()=>{ // 此方法默认不执行
    return count.value + 10;
})

// 当访问 getCount.value 的时候会执行会执行effect后面会说明
// getCount.value 执行一次
// getCount.value 还要不要缓存? 还要不要执行?不要了走缓存了
// 更新了 count.value = 100 就会执行了 getCount 从新被执行了但是不会立刻重新计算,为什么不会立即执行因为需要 getCount.value 手动触发一下 this.effect下面会解释
// 当我用 getCount.value的时候才会重新计算。

// computed 一定是一个effect 一定是一个lazy的  有缓存 我们执行的逻辑还不一定是 return count.value + 10; 这个因为改了 count.value没有马上执行 一定用了 scheduler
// getCount.value 多次访问值没有变 需要有个缓存 缓存的标识
创建computed.ts
// vue2 和 vue3 computed的原理是不相同的 计算属性也需要收集依赖。 vue2 计算属性没有收集依赖的能力的
export function computed(getterOrOptions // 可以是getter或者是一个options){
    let getter;
    let setter;
    if(isFunction(getterOrOptions)) {
        getter = getterOrOptions;
        setter = () => {};
    } else {
        const df = () => {}
        getter = getterOrOptions.getter || df;
        setter = getterOrOptions.setter || df;
    }
    // computed 的返回对象类似ref
    return new ComputedRefImpl(getter, setter);
}

class ComputedRefImpl {
    private notCache = true; // 默认取值不需要用缓存
    private _val = '';       // getCount = computed... getCount.value 的这个_val
    public effect = null;    // computed默认响应式用就是个effect
    constructor(public getter, public setter) {
        // 这里的这个effect是从effect.ts里面引入的
        this.effect = effect(() => { // 手动调用effect不走scheduler 收集的才走, 有个点需要明确说明一下 this.effect()跑的是 ()=>{} 内部的依赖发生了改变走的是 scheduler
              return  getter() // 靠着 effect对里面的依赖收集重新执行 getter
            },{
                lazy: true, // 默认别让他执行起来
                scheduler: () => {
                    // 修改值的时候在把缓存放开
                    // 如果修改了effect里面的值的时候 getter 值的时候因为有了scheduler所以()=>{}这里面的就不在执行了而执行scheduler
                    if(!this.notCache){ // 如果getter里面的值发生了改变依赖收集,触发到这里
                        this.notCache = true; // 注意一下这个时候并没有执行getter要等下面方位了才能执行。所以回答了上面的那个问题如果修改了不能立马响应。
                        
                        // 为了 --AAA(这个标识下面有一段例子,这里是要在更新的时候手动触发一下ComputedRefImpl这个类本身的属性的更新) 需要做依赖收集 访问的时候就收集它通知更新
                        trigger(this, TriggerOrTypes.SET, 'value')
                        
                    }
                
                }
            }
            
       }) // 默认创建了一个effect,这个会默认执行但是计算属性不会默认执行,所以需要加一个lazy    
    }
    
  get value() {
      // 比较核心的是在这里
      /*
          计算属性也需要收集依赖。 vue2 计算属性没有收集依赖的能力的
          计算属性本身就是一个effect
          computed = effect
          里面的依赖属性可以关联上computed
          首先需要生成一个effect
          创建完了一个类就生成一个effect
          public effect = '';
          取值的时候 effect 才执行
      */
      if(notCache){ // 没有缓存的时候才执行
          this._val = this.effect(); // 一取值就执行effect, 这个方法执行完毕后会把用户的执行结果拿回来,不走scheduler这个effect是返回的那个fn.
          this.notCache = false; // xx.value xxx.value 第二次就不走 effect了,这样多次取值取的永远是第一次执行后的结果。
      
      }
      // 为了 --AAA 需要做依赖收集 访问的时候就收集它
      tract(this, TrackOptTypes.Get,'value');
      
      return this._val;
  }
  
  set value(val = '') {        
    this._val = val;
    this.setter(this._val);
}

 /**
* const myAge = computed({
    get(){

    }
    set(){

    }
})
myAage.value = 1; 走到了上面的set里面了 先走 ComputedRefImpl 的 set 再走自定义的set
*/


}

// computed的两种实现方式
computed(() => {})
computed({
    set(){},
    get(){}
})

const myAge = compute(()=>{
    age.value
})

//需要考虑下 --AAA
myAge = effect(()=>{
    // 下面那个修改了这里不会更新因为这里没有用age
    myAge.value 
})这种情况

age.value = 500; // 需要修改 age.value 触发 myAge.value  这个effect的执行