Vue.js设计与实现学习总结(第四章5)调度执行

调度执行是指当 trigger 动作触发副作用函数重新执行的时候, 可以决定函数执行的时机, 次数和方式

以下面代码为例

const data = { foo: 1 }
const obj = new Proxy(data, /*----*/)

effect(() => {
  console.log(obj.foo)
})

obj.foo++

console.log('结束了')

代码输出为: 1 2 结束了

若此时需要将打印顺序调整为: 1 结束了 2 并且需要在不调整代码的情况下实现改需求就需要响应系统支持调度.可以为effect函数设计一个选项参数options, 允许用户指定调度器:

effect (
  () => {
  console.log(obj.foo)
  },
  {
    // 调度器 scheduler 是一个函数
    scheduler (fn) {
      //  ...
    }
  }
)

因此在副作用函数中也需要挂载options:

function effect (fn, options = {}) {
  const effectFn = () => {
    cleanup(effectFn)
    // 当调用 effect 注册副作用函数时, 将副作用函数复制给 activeEffect
    activeEffect = effectFn
    // 在调用副作用函数前将函数压栈
    effectStack.push(effectFn)
    fn()
    // 当前副作用函数执行结束后出栈并把activeEffect还原为之前的值
    effectStack.pop()
    activeEffect = effectStack[effectStack.length - 1]
  }
  // 将 options 挂载到effectFn上
  effectFn.options = options
  // activeEffect.deps 用于储存所有与副作用函数相关的依赖集合
  effectFn.deps = []
  // 执行副作用函数
  effectFn()
}

trigger函数中的副作用函数重新执行时, 就可以直接调用用户传递的调度器函数将控制权交给用户:

function trigger (target, key) {
  const depsMap = bucket.get(target)
  if (!depsMap) return
  const effects = depsMap.get(key)

  const effectsToRun = new Set()
  effects && effects.forEach(effectFn => {
    // 如果 trigger 触发执行的副作用函数与当前正在执行的函数相同则不触发执行
    if (effectFn !== activeEffect) {
      effectsToRun.add(effectFn)
    }
  })

  effectsToRun.forEach(effectFn => {
    // 如果一个副作用函数有调度器则调用改调度器, 并将副作用函数作为参数传递
    if (effectFn.options.scheduler) {
      effectFn.options.scheduler(effectFn)
    } else {
      // 否则直接执行副作用函数
      effectFn()
    }
  })
}

你可能感兴趣的:(前端vue.js)