加餐- 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 数据变更时,触发 triggertriggerEffect
  • 若 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、调度器、队列等模块协同工作,保证了数据驱动视图更新的高效和灵活。
相关推荐
Hilaku6 分钟前
深入background-image:你可能不知道的几个性能优化与高级技巧
前端·css
南岸月明8 分钟前
副业自媒体1年终于明白:为什么会表达的人,能量越来越强,更能赚到钱?
前端
Danny_FD22 分钟前
Vue + Element UI 实现模糊搜索自动补全
前端·javascript
gnip26 分钟前
闭包实现一个简单Vue3的状态管理
前端·javascript
斐济岛上有一只斐济32 分钟前
后端程序员的CSS复习
前端
Enddme35 分钟前
《面试必问!JavaScript 中this 全方位避坑指南 (含高频题解析)》
前端·javascript·面试
有梦想的程序员36 分钟前
微信小程序使用 Tailwind CSS version 3
前端
溟洵1 小时前
Qt 窗口 工具栏QToolBar、状态栏StatusBar
开发语言·前端·数据库·c++·后端·qt
用户2519162427111 小时前
Canvas之图像合成
前端·javascript·canvas
每天开心1 小时前
噜噜旅游App(4)——构建旅游智能客服模块,实现AI聊天
前端·微信小程序·前端框架