Watch
// 这样就实现了回调函数的立即执行功能。 由于回调函数是立即执行的, 所以第一次回调执行
// 时没有所谓的旧值, 因此此时回调函数的oldValue值为undefined, 这也是符合预期的。
// 除了指定回调函数为立即执行之外, 还可以通过其他选项参数来指定回调函数的执行时机,
// 例如在Vue.js 3 中使用flush 选项来指定:
watch(obj, () => {
console.log(' 变化了')
}, {
// 回调函数会在watch 创建时立即执行一次
flush: 'pre' // 还可以指定为' post'|' sync'
})
// flush本质上是在指定调度函数的执行时机。 前文讲解过如何在微任务队列中执行调度函数
// scheduler, 这与flush的功能相同。 当flush的值为' post' 时, 代表调度函数需要将副作用函数放到一个微任务队列中, 并等待DOM更新结束后再执行, 我们可以用如下代码进行模拟:
function watch(source, cb, options = {}) {
let getter
if (typeof source == ' function') {
getter = source
} else {
getter = () => traverse(source)
}
let oldValue, newValue
const job = () => {
newValue = effectfn()
cb(newValue, oldValue)
oldValue = newValue
}
const effectFn = effect(
//执行 getter
() => getter(), {
lazy: true,
scheduler: () => {
//在调度函数中判断 flush 是否为' post', 如果是, 将其放到微任务队列中执行
if (options.flush === 'post') {
const p = Promise.resolve()
p.then(job)
} else {
job()
}
}
})
if (options.immediate) {
job()
} else {
oldValue = effectFn()
}
}
// immediate>sync===default>pre>post
// 如以上代码所示, 我们修改了调度器函数 scheduler 的实现方式, 在调度器函数内检测options.flush的值是否为post,
// 如果是, 则将job函数放到微任务队列中, 从而实现异步延迟执行; 否则直接执行 job函数,
// 这本质上相当于' sync' 的实现机制, 即同步执行。 对于options.flush的值为' pre' 的情况,
// 我们暂时还没有办法模拟, 因为这涉及组件的更新时机, 其中' pre' 和' post' 原本的语义指的就是组件更新前和更新后, 不过这并不影响我们理解如何控制回调函数的更新时机。
Last updated