5.vue设计与实现-调度器

769 阅读2分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

上篇我们实现了 vue设计与实现-避免无限递归循环,本篇我们聊一聊调度器。

为什么需要调度器

在前几篇文章中,我们都是通过执行effect函数,在effect函数中读取代理的数据,收集依赖。然后修改数据,触发依赖。

我们在实际开发的业务中,可能不想在修改数据后,立马触发依赖。

1.比如我修改代理数据newObj.cout的值,我修改了10次,我只想在最后调用一次就可以了。

2.比如我们在想控制触发依赖的时机,比如获取到某个数据之后,再去触发依赖

调度器的功能就是为了将effect的使用权交给用户,从而控制依赖触发的次数和时机

例子

const obj={
    count:1
}
const newObj=new Proxy(obj,{
    get(){
        ...
    },
    set(){
        ...
    }
})
effect(()=>{
   console.log(newObj.count);
})
 newObj.count=3
 console.log(newObj.count)
 console,log("输出新值")
 

如上代码所示,上面的代码会依次输出

1 3 ”输出新值“

但是我们理想的输出是 1 “输出新值” “3”,我们要如何才能做到呢

思考

我们想要在合适的时机去触发effect,肯定要从trigger入手,目前我们是获取到effectFn()之后立马触发的,我们可以在这里做一些“文章”


function trigger(target,key){
    let depMaps=bucket.get(target)
    let deps=depMaps.get(key)
    let effectTorun=new Set(deps)
    effectTorun.forEach(effectFn => {
        //增加防止无限递归循环的代码
        if(effectFn!==activeEffect){ 
        //我们可以在这里做一些“文章”
           effectFn() 
           
        }   
    });

}

大胆设想一下:比如我们有一个函数schedule,这个函数可以将effectFn 传进去,然后去控制effectFn的执行时机和执行次数。

可是这个函数要在哪里定义呢? 要怎么拿到呢?

方案

先说方案;

这个schedule 可以通作为effect函数的参数传入 然后作为effectFn的一个属性,这样,在trigger的时候就可以拿到用户传入的schedule了

一.effect代码如下

let effect=(fn,option={})=>{
    //清空effectFn 的deps
    const effectFn=()=>{
        cleanUp(effectFn)
        activeEffect=effectFn
        effectStack.push(effectFn)
        const res=fn()
        effectStack.pop()
        activeEffect=effectStack[effectStack.length-1]
        return res
    }
    effectFn.deps=[]
    effectFn.option=option
    if(option.lazy){
        return effectFn
       
    }else{
        effectFn()
    } 
  
}

注意看 option 相关代码

1.这里将option 初始化为一个空对象

2.将option 挂载在effectFn的option上面

二.再来看trigger的代码

function trigger(target,key){
    let depMaps=bucket.get(target)
    let deps=depMaps.get(key)
    let effectTorun=new Set(deps)
    effectTorun.forEach(effectFn => {
        //增加防止无限递归循环的代码
        if(effectFn!==activeEffect){   
            if(effectFn.option.schedule){
                effectFn.option.schedule(effectFn)
            }else{
                effectFn()  
            }      
                     
        }   
    });

}

这里判断option对象里面有没有schedule,如果有,则将effect使用权交给schedule,如果没有,则会自动执行

如何使用呢?

三.使用的代码如下

effect(()=>{
    console.log(newObj.count)
},{
    schedule:(fn)=>{
        setTimeout(() => {
            fn()
        }, 500);
    }

})
newObj.count=3
console.log("输出新值")

这样,我们输出的顺序就是

1 “输出新值” 3

当然,我们熟知的computed 和watch 都会使用到schedule