今天继续总结学习Vue3.0的基本原理。computed(), 计算属性,在vue3.0中computed可以传入一个getter函数也可以传入getter与setter两个函数。
在computed模块中,首先处理处理用户传入的对象,在接收对象时分为一个参数与两个参数的情况,最终返回一个computed对象,在处理传入的对象时其实是调用effect模块,让其成为响应式的数据,所以computed也是另一种形式的effect。因此,在effect模块中要加入对computed类型的处理,分而治之,并且computed优先于一般的effect执行。大概思路就是这样,直接上代码:
export
function
computed(getterOrOptions) {
let getter; //接收传入的参数
let setter;
if (isFunction(getterOrOptions)) { //isFunction是自定义的工具函数
getter = getterOrOptions;
setter = ()=>{}
} else {
getter = getterOrOptions.get;
setter = getterOrOptions.set;
}
let dirty = true;//缓存机制的标志 控制是否触发依赖,达到缓存效果
let computed;
let runner = effect(getter, { //调用effect
lazy: true, //首次不执行
computed: true,//计算属性标识
scheduler: ()=>{ //在有新的依赖触发时effect负责执行
if (!dirty) {
dirty = true;//依赖值变化后 没有缓存数据了
trigger(computed,TriggerOpTypes.SET,'value') //触发依赖
}
}
})
let value;
computed = {
get value() {
if (dirty) { //多次取值 不会重复执行effect
value = runner();
dirty = false;
track(computed,TriggerOpTypes.GET,'value')//收集计算结果的依赖,使其成为响应式数据
}
return value;
},
set value(newValue) {
setter(newValue)
}
}
return computed;
}
effect中的配合
export
function
trigger(target, type, key, value, oldValue) {
//获取当前对应的map
const depsMap = targetMap.get(target);
if (!depsMap) {
return; //说明没有被收集过依赖
}
//计算属性优先于effect执行
const effects = new
Set(); //装普通effect
const computedRunners = new
Set(); //装计算属性的effect
//具体执行effect集合的方法
// const run = (effect) => {
// if (effect) {
// effect.forEach(effect => effect())
// }
// }
const run = (effect) => {
if (effect.options.scheduler) {
effect.options.scheduler()
} else {
effect();
}
}
let add = (effectsToAdd)=>{
if (effectsToAdd) {
effectsToAdd.forEach(effect => {
if (effect.options.computed) {
computedRunners.add(effect)
} else {
effects.add(effect)
}
})
}
}
if (key !== null) {
add(depsMap.get(key));
}
if (type === TriggerOpTypes.ADD) {
add(depsMap.get(Array.isArray(target) ? 'length' : ''))
}
computedRunners.forEach(run)
effects.forEach(run)
}
在触发effect中,增加了两个存储computed和effect的集合,通过add方法所有的依赖map中的effect分类,最后分开执行;
computed的实现基础还是effect,可以理解为effect的一种变形。