watch侦听器一直是 Vue3 响应式系统中不可或缺的组成部分,他能实现对响应式变量的监听,有immediate、deep和 flush 等配置。但watch终究不是魔法,你知道项目中每一个watch的背后都发生了什么吗?
本文不会粘贴大量源码,但会从流程图的角度讲解watch的原理实现,我会将watch的实现逻辑抽象成5个关键步骤,让你清晰的了解到 Vue3 的 watch 原理原来这么简单。
在阅读本文之前,你需要对 Vue3 响应式原理有一定了解。如果你还不太了解,笔者前文 《干货说Vue3响应式实现》一定要看看~
一. watch监听器需要解决的问题
在拆解步骤前,你应该知道watch的实现需要解决哪些关键需求。
Watch api 整体的流程
二. Step1:第一个参数处理成getter函数
收集依赖肯定是要在effect函数中执行的,第一个参数可以多种多样,所以要先把第一个参数转换成getter函数。
为什么 source 是 reactive 的时候自动设置为 deep,是因为直接监听一个经过 reactive API处理返回的 proxy 对象是没有任何意义的,我们需要的是对source的属性或者深层属性的变更进行监听。 这个过程会经过一个 traverse 的过程,去递归的访问 reavtive 对象的所有子属性,使得这些子属性都设置成响应式,并将当前watch effect收集进自己的depSet。
三. Step2:创建 watch effect
现在有了 getter,有了 scheduler,只要getter作为effect的回调,scheduler作为effect的option配置,就能实现 watch effect。
const runner = effect(getter, {
// 延时执行
lazy: true,
// computed effect 可以优先于普通的 effect 先运行,比如组件渲染的 effect
computed: true,
onTrack,
onTrigger,
scheduler: applyCb ? () => scheduler(applyCb) : scheduler
})
四. Step3:创建 scheduler
Watch option 有三种配置flush:sync、pre、post。
queue中的applyCb会在模板更新前执行,queuePostRenderEffecr中的则会在模板更新后执行。
五. Step4:构建 applyCb()
这个 applyCb 比较简单,实际上就是给回调函数包装一层errorHandling,并在执行前,执行getter进行求新值。