从vue2.x的源码中,我们知道computed本质上也是Watch类的一个实例。只是他在初始化时,传的lazy参数为true,使的它能够懒加载。从vue3.x的源码中我们可以看出它也是依赖effect来实现的。vue3.x里面的effect本质上就是vue2.x中的watch。以下是vue3.xcomputed的源码分析。

# 1、set、get钩子

  计算属性里默认有get钩子。用于获取修改后的响应式数据。set钩子可有可没有。所以要注意以下几点

  • 判断传入computed(arg)函数中的参数到底是以对象的形式还是以函数的形式。
    • 如果是函数,我们就得自己构造一个set钩子函数(空函数)。
    • 如果是对象,就将setget钩子函数分别指向传入的对象。
  • 计算属性不会立即执行,只会在获取数据和它依赖的数据被修改后,才会执行。
  • computed函数中会调用effect函数,将get钩子函数作为fn传入,同时传入包括lazycomptued属性以及一个调度器函数。该调度器函数会在计算属性中的属性发生变化时执行。
  • 在计算属性中有一个dirty属性,默认为true。在get钩子中会将dirty修改为false,同时将。computedvalue属性进行依赖收集。
  • effect中的trigger函数中,通过对传入的options进行判断,如果有computed属性的就是计算属性。在执行更新操作时,判断options中是否scheduler属性,有这个属性的话,就是computed属性。注意: 计算属性的优先级高于一般effect
//computed.js
let getter,setter;
export function computed(getterOrOptions){
    if(isFunction(getterOrOptions)){
        getter=getterOrOptions;
        setter=()=>{};
    }else{
        getter=getterOrOptions.get;
        setter=getterOrOptions.set;
    }
    let computed;
    let value;
    let dirty=true;
    //执行effect
    const ruuner=effect(getter,{
        lazy:true,
        computed:true,
        scheduler:()=>{
            if(!dirty){
                dirty=true;
                //触发更新
                trigger(computed,TriggerOpTypes.SET,'value')
            }
        }
    })
    computed={
        get value(){
            if(dirty){
                value=ruuner();
                dirty=false;
                //在外面获取值时收集依赖
                /**
                 * const myage=computed(()=>{
                 *      return state.age*3
                 * })
                 * myage.value;//在这里触发依赖收集
                 */
                track(computed,TrackOpTypes.GET,'value');
            }
            return value;
        },
        set value(newValue){
            setter(newValue);
        }
    }

    return computed;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
//effect.js
//触发更新
export function trigger(target,type,key,value,oldValue){
    const depsMap=targetMap.get(target);
    if(!depsMap) return;
    //计算属性要优先于effect执行
    const efects=new Set();
    const computedRunners=new Set();
    const add=effectToAdd=>{
        if(effectToAdd){
            effectToAdd.forEach(effect=>{
                //判断是是否是计算属性
                if(effect.options.computed){
                    computedRunners.add(effect)
                }else{
                    effects.add(effect)
                }
            })
        }
    }
    //判断是否是更新操作、删除和添加的操作
    if(key!==null){
        add(depsMap.get(key));
    }
    //判断是否是新增操作,新增操作
    if(type===TriggerOpTypes.ADD){ //对数组新增属性,会触发length对应的依赖
        add(depsMap.get(Array.isArray(target)?'length':""));
    }
    const run=effect=>{
        //判断是否是计算属性
        if(effect.options.scheduler){
            effect.options.scheduler();
        }else{
            effect();
        }
    }
    computedRunners.forEach(effect=>run(effect));
    effects.forEach(effect=>run(effect));
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
Last Updated: 12/27/2020, 3:10:55 PM