一、watch 侦听一个或多个响应式数据源,并在数据源变化时调用所给的回调函数
第一个参数可以是不同形式的 “数据源”:
它可以是一个 ref (包括计算属性)、一个响应式对象、一个 getter 函数、或多个数据源组成的数组
第二个参数是在发生变化时要调用的回调函数:
这个回调函数接受三个参数:新值、旧值,以及一个用于注册副作用清理的回调函数
第三个可选的参数是一个对象,支持以下这些选项:
immediate:是否立即执行回调
deep:是否深度监听
flush:调度的时机
onTrack / onTrigger:调试侦听器的依赖
once:回调函数只会运行一次
doWatch 是 watch 的核心实现函数,负责处理各种情况:
Getter 函数的创建:
这里处理不同类型的 source,生成相应的 getter 函数。getter 函数的作用是提取出需要被监听的值
如果 source 是 ref,则 getter 返回 source.value。如果是 reactive 对象,则使用 reactiveGetter 处理。NOOP 是一个空操作函数。等等,以此类推
调度和执行:
job 是当数据变化时被调度执行的函数
当数据变化时,effect.run() 会执行 getter,并判断是否需要触发回调函数
如果有回调函数 cb,则在变化时调用它,并传入新的值和旧的值
ReactiveEffect 的使用 (通过创建 ReactiveEffect 来监听数据的变化是 Watch 函数实现的原理):
通过 flush 选项决定调度方式
immediate 选项: 如果 immediate 为 true,那么在 watch 初始化时会立即执行 job,这意味着在第一次渲染时就会执行回调
如果 immediate 为 false,则通过 effect.run() 手动执行一次 getter,以捕获初始值
最后,doWatch 函数返回 unwatch 函数,这样调用者可以随时停止监听
二、watchEffect 立即运行一个函数,同时响应式地追踪其依赖,并在依赖更改时重新执行
第一个参数就是要运行的副作用函数。这个副作用函数的参数也是一个函数,用来注册清理回调
第二个参数是一个可选的选项,可以用来调整副作用的刷新时机或调试副作用的依赖
可以发现 doWatch 同时是 watch 和 watchEffect 的核心实现函数,它根据不同的参数执行不同的逻辑
effect 参数则是一个副作用函数,cb(回调函数)为 null,所以这是一个纯副作用的监听逻辑
doWatch 函数中与 watchEffect 相关的关键部分:
当 watchEffect 被调用时,doWatch 会立即运行 effect.run(),执行副作用函数,并收集所有在执行过程中使用到的响应式依赖
核心原理也是通过 ReactiveEffect 实现的,ReactiveEffect 会在副作用函数执行时自动收集依赖,并在依赖变化时重新执行该函数
三、watch 与 watchEffect() 相比
watch 和 watchEffect 的使用示例::
使用 watch 时,你需要明确指定哪些数据需要监听,并且可以通过回调函数获取新旧值来对数据变化进行精确处理
使用 watchEffect 时,Vue 会自动追踪所有在副作用函数中使用到的响应式数据,并在它们发生变化时重新执行整个函数,适合处理简单的自动化副作用
watch 适用于需要精确控制何时以及如何响应数据变化的场景,watchEffect 则更加自动化,所有在副作用函数中使用到的响应式数据都会被自动追踪
四、其他
Tips:
(1) 关于 ReactiveEffect,在本文中不会继续深入,所以如果你是初次阅读 Vue 3 源码,请务必先了解 ReactiveEffect,在 Watch 中我们可以发现 ReactiveEffect 是从 @vue/reactivity → './effect' 中导入的,你可以从这个包中找到,最好可以结合 reactive 实现一起阅读
(2) 在阅读的时候,建议了解实现的基本核心逻辑就可以,意思就是先考虑代码执行的正常情况,额外情况先考虑跳过,因为 Vue 源码里面各种条件判断特别多,而这些判断很多时候是为处理各种情况,有时候我们没有考虑到这么多种情况,容易跟不下去
学习的记录,仅供参考