从vue2.x
的源码中,我们知道computed
本质上也是Watch
类的一个实例。只是他在初始化时,传的lazy
参数为true
,使的它能够懒加载。从vue3.x
的源码中我们可以看出它也是依赖effect
来实现的。vue3.x
里面的effect
本质上就是vue2.x
中的watch
。以下是vue3.x
中computed
的源码分析。
# 1、set、get钩子
计算属性里默认有get
钩子。用于获取修改后的响应式数据。set
钩子可有可没有。所以要注意以下几点
- 判断传入
computed(arg)
函数中的参数到底是以对象的形式还是以函数的形式。- 如果是函数,我们就得自己构造一个
set
钩子函数(空函数)。 - 如果是对象,就将
set
和get
钩子函数分别指向传入的对象。
- 如果是函数,我们就得自己构造一个
- 计算属性不会立即执行,只会在获取数据和它依赖的数据被修改后,才会执行。
- 在
computed
函数中会调用effect
函数,将get
钩子函数作为fn
传入,同时传入包括lazy
和comptued
属性以及一个调度器函数。该调度器函数会在计算属性中的属性发生变化时执行。 - 在计算属性中有一个
dirty
属性,默认为true
。在get
钩子中会将dirty
修改为false
,同时将。computed
的value
属性进行依赖收集。 - 在
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
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
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