精读《Vuejs设计与实现》days20

146 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第29天,点击查看活动详情

4.7 调度执行

在前面几章,我们主要目标在完成一个基本可用的响应式,和修复响应式的bug.这一章会继续深入去探讨响应式的一些功能,可以叫完善的我们的响应式。

  • 什么是调度 我们想象这样一个需要,我们需要一次打印出
1
2
结束

那么我们的代码,大概会是这样

const data = reactive({num:1})
effect(()=>{
    consoloe.log(data.num)
})
data.num++
consoloe.log('结束')

如果我们需要的顺序变了,那么我们就需要修改代码才能调整顺序,这种其实叫做耦合,为什么不把执行的顺序作为外部输入呢?

这样,我们每次修改需求,其实就是修改外部的一个参数,而对于功能本身是不动的,这就是解耦,说高级一点就是依赖注入。

所以,在Vue.js中,设计了一个调度器,就是这样解决问题的。

我们在调用effect函数的时候,给他加上第二个参数,这个参数事一个对象,里面有一个scheduler,由于调度器其实本质上是控制流程的,因此它毫无疑问的是一个函数

effect(
    ()=>{consoloe.log(data.num)},
    {scheduler(fn){}}
)

因此我们需要修改effect

const effect((fn,options={})=>{
    const runEffect=()=>{
        cleanup(runEffect) // 清除重复依赖
        activeEffect = runEffect
        effectStack.unshift(runEffect) //将副作用函数压入栈中
        fn()
        effectStack.pop() // 执行完成后,出栈
        activeEffect = effectStack.at(-1) //将activeEffect对准栈底
    }
    runEffect.deps=[]
    runEffect.options = options //新增,将options挂到对应的副作用函数上
    runEffect()
})

那么,这个函数在哪里使用呢?自然是trigger时,当触发副作用函数的时候,我们可以去判断用户是否传入了调度器,如果传入我们就执行调度器,如果没有,那么就按顺序遍历即可。

我这里就不贴上之前写的trigger函数了,等把这一篇写完,我再整理代码,并写上详细注释

  // 旧代码,仅是遍历执行
 // effectToRun.forEach(fn=>fn())
 effectToRun.forEach(fn=>{
     //判断是否存在调度器,由于options已存在默认值,所以这里可以这样写
     if(fn.options.scheduler){
         fn.options.scheduler(fn)
     } else {
      fn()
     }
 })

是不是感觉很简单,确实实现起来很简单,但是我们更应该学习这个思想。

依赖注入又叫控制反转,其实从这个名字你就能大概猜到它的意思,其实就是把控制权或者依赖从外部传进来,大部分时候就是函数的传参,这样可以实现更好的解耦,比如axios的拦截器,也是这种思想。