Vue 的
watch
,其实就是一个"观察者"。它悄悄盯着某个响应式数据,一旦它变了,就执行一段你写好的逻辑。
比如:
javascriptwatch(count, (newVal, oldVal) => { console.log('count 改了!', newVal, oldVal) })
这个逻辑在源码里是怎么实现的呢?我们一步步来看。
🧠 一、核心思想(一句话总结)
watch
= 让一个函数自动在依赖变化时重新执行。
也就是说,Vue 会:
- 找出你依赖了哪些响应式数据;
- 把这些数据的变化和一个回调函数"绑定";
- 一旦依赖变动 → 回调自动执行。
⚙️ 二、watch
在干什么?(通俗讲解)
我们先从用户角度看你调用 watch
时到底发生了什么:
你做的事情 | Vue 内部在做的事情 |
---|---|
传入一个 source (ref、reactive 或函数) |
把它转换成一个能"取值"的 getter 函数 |
提供一个回调 cb |
当 getter 的依赖变化时,执行这个回调 |
设置选项(如 deep , immediate ) |
控制是否深度追踪、是否立即执行一次 |
调用 watch |
Vue 创建一个 "反应函数"(ReactiveEffect) 来追踪依赖 |
数据变化 | ReactiveEffect 被触发 → 调用你的回调 |
一句话总结:
Vue 创建了一个专门盯着你"source"的小助手,一旦 source 的依赖变化,它就会自动执行你给的回调。
🧩 三、主要步骤详解(配图理解)
🥇 第一步:把 source
变成能取值的函数 getter
Vue 会根据 source
类型决定"怎么取值":
source 类型 | getter 做的事 |
---|---|
ref | 返回 .value |
reactive 对象 | 遍历所有属性(深度追踪) |
函数 | 直接执行函数 |
数组 | 依次处理数组里的每个 ref/reactive |
👉 目的:无论用户传什么类型,Vue 都能得到一个统一的"取值函数 getter"。
🥈 第二步:包成一个 ReactiveEffect
ini
effect = new ReactiveEffect(getter)
这个对象可以理解为一个"反应式执行器":
- 当依赖变化时,它会自动重新运行 getter;
- 它内部帮你完成了依赖收集、变化触发;
- 每次重新运行时,Vue 就能检测新旧值。
💡可以理解为:
"ReactiveEffect 就是 Vue 的大脑,记得谁依赖了谁。"
🥉 第三步:定义一个 job(变化时要做的事情)
scss
const job = () => {
const newValue = effect.run() // 再次执行 getter 得到新值
if (hasChanged(newValue, oldValue) || deep) {
cleanup?.() // 执行上一次注册的清理函数
cb(newValue, oldValue, onCleanup) // 调用用户的回调
oldValue = newValue
}
}
它的作用就是:
- 拿到新值;
- 比较和旧值是否不同;
- 如果不同,就执行回调;
- 更新旧值。
🧼 第四步:注册清理函数
Vue 允许你在 watch
回调里注册一个清理函数,比如:
scss
watch(data, (newVal, oldVal, onCleanup) => {
const timer = setTimeout(...)
onCleanup(() => clearTimeout(timer))
})
源码里:
rust
onWatcherCleanup(fn, false, effect)
在下一次执行
cb
之前,Vue 会自动执行上次注册的清理逻辑。
🕹️ 第五步:调度执行与控制
-
Vue 支持自定义调度(scheduler):
- 让回调在 "微任务" 或 "渲染后" 执行;
-
还支持:
immediate
:立刻执行一次;once
:只执行一次;pause()
/resume()
/stop()
:手动控制监听状态。
🪄 四、整体流程图
vbnet
watch(source, cb, options)
├─► Step 1: 构造 getter(根据 source 类型)
├─► Step 2: 创建 ReactiveEffect(追踪依赖)
├─► Step 3: 定义 job(变化时要执行的函数)
├─► Step 4: 注册清理函数(下一次执行前清理旧逻辑)
├─► Step 5: 配置 scheduler(决定执行时机)
├─► Step 6: 初始化执行(是否 immediate)
└─► Step 7: 返回控制句柄(pause/resume/stop)
🧾 五、总逻辑表(更易读版)
阶段 | 关键逻辑 | 作用 | 举例说明 |
---|---|---|---|
1️⃣ 构造 getter | 统一取值方式 | 无论是 ref、reactive 还是函数,都能被监听 | 监听 count.value 、obj.a 都行 |
2️⃣ 创建 ReactiveEffect | 追踪依赖 | 当依赖数据变化时自动触发 | 改 count.value → 回调自动执行 |
3️⃣ 定义 job | 回调逻辑封装 | 比较新旧值、执行回调 | 打印新旧值 |
4️⃣ 注册清理函数 | 回调前清理上一次副作用 | 清除定时器、取消请求 | 典型:watch 中用 onCleanup() |
5️⃣ 调度执行 | 控制何时运行回调 | 可延后执行 | 如 flush: 'post' |
6️⃣ 初始化执行 | immediate/once | 是否立即执行、只执行一次 | 页面加载立刻监听 |
7️⃣ 返回控制接口 | 控制 watch 状态 | 暂停、恢复、停止监听 | 动态控制监听开关 |
🪶 六、总结一句话
watch
的本质:
Vue 帮你建了一个反应式"监听器",一旦依赖变动,它就自动重新执行。
🔹 它通过:
ReactiveEffect
实现依赖追踪;getter
统一不同数据类型;job
封装执行逻辑;onWatcherCleanup
清理副作用;scheduler
控制执行时机。
从而形成一个完整、灵活、可控的监听系统。
本文内容由人工智能生成,仅供学习与参考使用,请在实际应用中结合自身情况进行判断。