🌿 一文看懂 Vue 3 的 watch 源码:从原理到流程

详细源码注释

Vue 的 watch,其实就是一个"观察者"。

它悄悄盯着某个响应式数据,一旦它变了,就执行一段你写好的逻辑。

比如:

javascript 复制代码
watch(count, (newVal, oldVal) => {
  console.log('count 改了!', newVal, oldVal)
})

这个逻辑在源码里是怎么实现的呢?我们一步步来看。


🧠 一、核心思想(一句话总结)

watch = 让一个函数自动在依赖变化时重新执行。

也就是说,Vue 会:

  1. 找出你依赖了哪些响应式数据;
  2. 把这些数据的变化和一个回调函数"绑定";
  3. 一旦依赖变动 → 回调自动执行。

⚙️ 二、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
  }
}

它的作用就是:

  1. 拿到新值;
  2. 比较和旧值是否不同;
  3. 如果不同,就执行回调;
  4. 更新旧值。

🧼 第四步:注册清理函数

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.valueobj.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 控制执行时机。

从而形成一个完整、灵活、可控的监听系统。


本文内容由人工智能生成,仅供学习与参考使用,请在实际应用中结合自身情况进行判断。

相关推荐
军军君017 分钟前
Three.js基础功能学习十五:智能黑板实现实例二
开发语言·前端·javascript·vue.js·3d·threejs·三维
IT枫斗者15 分钟前
构建具有执行功能的 AI Agent:基于工作记忆的任务规划与元认知监控架构
android·前端·vue.js·spring boot·后端·架构
hotlinhao16 分钟前
Nginx rewrite last 与 redirect 的区别——Vue history 模式短链接踩坑记录
前端·vue.js·nginx
ZC跨境爬虫19 分钟前
海南大学交友平台开发实战day7(实现核心匹配算法+解决JSON请求报错问题)
前端·python·算法·html·json
下北沢美食家22 分钟前
CSS面试题2
前端·css
weixin_4617694028 分钟前
npm create vue@latest 错误
前端·vue.js·npm
WindrunnerMax30 分钟前
从零实现富文本编辑器#13-React非编辑节点的内容渲染
前端·架构·github
四千岁30 分钟前
Ollama+OpenWebUI 最佳组合:本地大模型可视化交互方案
前端·javascript·后端
写不来代码的草莓熊32 分钟前
el-date-picker ,自定义输入数字自动转换显示yyyy-mm-dd HH:mm:ss格式
前端·javascript·vue.js
ssshooter32 分钟前
Tauri 应用苹果签名踩坑实录
前端·架构·全栈