加餐- Vue3中响应式的 watch 流程实现解读【手摸手带你实现一个vue3】

大家好,我是作曲家种太阳,今天我们来手把手去看看 Vue 3 响应式系统中非常核心的一部分:watch 是怎么个执行流程和触发流程,深入了解watch的原理和设计理念

watch 是什么?

watch 是 Vue3 中用于监听响应式数据变化的 API,当被监听的数据发生变化时,它会触发用户定义的回调函数。它适用于需要在数据变化时执行异步或副作用逻辑的场景。

watch 接收三个参数:

  1. source:响应式对象、getter 函数、ref 或数组等。
  2. cb:当监听的数据变化时要执行的回调函数。
  3. options :配置项,如:
    • immediate: 是否立即执行一次 cb。
    • deep: 是否进行深度监听。

源码解读:watch 的执行过程

1️⃣ watch 函数入口

  • 源码路径:packages/runtime-core/src/apiWatch.ts
  • 核心调用的是 doWatch 方法

watch 中做了以下处理:

  • 判断 source 类型,生成 getter(一般是 () => source)
  • 若为 reactive 对象,默认开启 deep 监听
  • 创建 baseGetter
  • 创建 job 函数:数据变化时触发的回调逻辑
  • 创建调度器 scheduler = () => queuePreFlushCb(job)
  • 使用 ReactiveEffect 包装 getter,并附带 scheduler
  • 返回一个 teardown 回调,用于后续停止监听

2️⃣ 响应式数据触发 setter

  • 当 reactive 数据变更时,触发 trigger → triggerEffect
  • 若 effect 有 scheduler,则执行 scheduler()
  • 此 scheduler 调用 queuePreFlushCb(job),将任务放入微任务队列

3️⃣ queuePreFlushCb 函数执行

ts 复制代码
queuePreFlushCb(job) → queueCb(job, pendingPreFlushCbs)
→ queueFlush() → Promise.resolve().then(flushJobs)

此过程核心在于:

  • 将 job 推入队列 pendingPreFlushCbs
  • 使用微任务 Promise 确保异步执行 flushJobs

4️⃣ flushJobs 函数

  • 触发 flushPreFlushCbs → 执行所有 pendingPreFlushCbs
  • job 被调用 → 触发 effect.run()

此时 effect.run() 会重新调用 getter,获取新的值。

  • 然后调用 callWithAsyncErrorHandling(cb, ...) 执行回调函数 cb
  • 参数为:(newValue, oldValue)

watch 核心组成

整个 watch 的运行分为 4 大块:

  1. watch 函数初始化逻辑
  2. reactive 的 setter 触发 trigger
  3. 调度系统:queuePreFlushCb + flushJobs
  4. job 执行,运行 effect,触发 cb 回调

调度系统实现机制

watch 中使用了 Vue3 的调度系统,该系统包含两个核心机制:

💤 lazy 懒执行

当 effect 设置 lazy: true 时,不会立即执行 run() 方法,而是等到手动触发或依赖变动时才执行。

🧠 scheduler 调度器

调度器控制两件事:

  1. 控制执行顺序: 利用 queue + Promise.then 实现异步控制,确保 DOM 更新前统一 flush。

  2. 控制执行规则: 例如:如果连续赋值多次,只执行一次 effect(节流/去抖)

实现关键:

ts 复制代码
export function queuePreFlushCb(cb: Function) {
  queueCb(cb, pendingPreFlushCbs)
}

function queueFlush() {
  if (!isFlushPending) {
    isFlushPending = true
    currentFlushPromise = resolvedPromise.then(flushJobs)
  }
}

function flushJobs() {
  isFlushPending = false
  flushPreFlushCbs()
}

function flushPreFlushCbs() {
  const cbs = [...new Set(pendingPreFlushCbs)]
  pendingPreFlushCbs.length = 0
  for (const cb of cbs) cb()
}

通过微任务机制控制更新节奏,实现 watch 调度的异步安全。

总结

  • watch 是用于观察响应式数据变化的 API,适合执行副作用逻辑。
  • 它底层基于 ReactiveEffect 创建 effect,并使用 scheduler 控制回调时机。
  • watch 的调度机制基于异步队列和微任务,确保回调顺序和性能。
  • Vue3 的响应式系统通过 effect、调度器、队列等模块协同工作,保证了数据驱动视图更新的高效和灵活。
相关推荐
用户461169657284 分钟前
网站 HTTPS 不稳定?证书排查指南
前端
n876515 分钟前
Playwright Java WEB UI 自动化实现框架与逻辑
java·前端·ui
keyipatience17 分钟前
TypeScript学习(1)
前端·javascript·学习·typescript·html·css3
xifangge20251 小时前
基于 JavaScript 实现 CNC 数控切削参数与螺纹公差纯前端核算引擎
开发语言·前端·javascript
wangruofeng1 小时前
2026 Web 技术选型指南,AI 时代如何选择适合项目的技术栈
前端·aigc·ai编程
honkun61 小时前
vue 表格组件 vxe-table 修改滚动条样式,修改滚动条样式,统一宽度、颜色与自动隐藏
前端·javascript·vue.js
爱勇宝2 小时前
研究数字孪生的这半年,做了个 Demo,也有了一些新想法
前端·程序员
不可能片场2 小时前
工程实践 一个缓存把选片从十分钟压到一分钟
前端·electron
前端小万2 小时前
探索副业五个月,最终以放弃告终
前端·后端·面试
FRing032 小时前
JavaScript入门到精通二十四(正则表达式、元字符、修饰符)
开发语言·前端·javascript·正则表达式