第 31 题:Vue3 中 watchEffect 的原理(依赖自动追踪 + 清理机制 + ReactiveEffect 全流程)

第 30 题:Vue3 中 watchEffect 的原理(依赖自动追踪 + 清理机制 + ReactiveEffect 全流程)

依旧采用:
核心回答 → 深度原理 → 源码流程图 → 例子 → 面试追问 → 金牌总结


🎯 一、核心回答(面试必答版)

watchEffect 的核心原理是:

自动依赖收集 + 自动触发 + 自动清理

它会立即执行一次回调,并在访问响应式数据时收集依赖;当依赖变化时自动重新执行。

watch 最大区别:

  • watchEffect自动收集依赖(用到了什么就监听什么)
  • watch需要你手动指定依赖源

内部依靠:

  • ReactiveEffect(effect 实例)
  • track() / trigger()(依赖收集 / 触发机制)
  • cleanupEffect()(清理旧副作用)

🎯 二、深度原理:watchEffect 内部到底做了什么?

当你写:

scss 复制代码
watchEffect(() => {
  console.log(obj.count)
})

Vue 做了以下事情:


1️⃣ 创建一个 ReactiveEffect 实例

php 复制代码
const effect = new ReactiveEffect(fn, scheduler)

ReactiveEffect 是响应式系统的核心:

  • 记录当前正在运行的副作用
  • 维护依赖集合 deps
  • 控制执行时机

2️⃣ 立即执行 fn(与 watch 不同)

watchEffect 不需要指定依赖

它会先运行一次回调,这一步非常关键:

scss 复制代码
effect.run()

3️⃣ 执行 fn 时访问响应式数据 → track()

当执行 obj.count 时:

scss 复制代码
track(obj, 'count')

Vue 将:

  • effect 记录到 obj.count 的依赖列表中
  • obj.count 也记录它依赖的所有 effect

完整结构是这样:

css 复制代码
depMap: {
  obj: {
    count: [effect]
  }
}

4️⃣ 当 count 改变时 → trigger()

当你写:

复制代码
obj.count++

Vue 内部执行:

scss 复制代码
trigger(obj, 'count')

找到所有依赖(effect),并调度执行。


5️⃣ 调度执行(scheduler)

watchEffect 会给 effect 注入一个 scheduler:

scss 复制代码
scheduler = () => queueEffect(effect.run)

作用:

  • 批量执行(避免重复 run)
  • nextTick 后执行
  • 控制执行顺序

6️⃣ cleanup 清理机制

每次执行 effect 前:

scss 复制代码
cleanupEffect(effect)

作用:

  • 删除 effect 对旧依赖的绑定
  • 避免死循环
  • 多次执行会重新收集依赖

你可以自己写收集变化依赖:

scss 复制代码
watchEffect(() => {
  if (flag.value) {
    console.log(a.value)
  } else {
    console.log(b.value)
  }
})

第一次访问 a

第二次访问 b

cleanupEffect 会清理旧 a 的依赖。


🎯 三、源码级流程图(强烈推荐背)

scss 复制代码
watchEffect(fn)
  ↓
ReactiveEffect(fn)
  ↓
effect.run()
  ↓(运行 fn)
访问响应式数据 → track(effect)
  ↓
数据变化 → trigger()
  ↓
scheduler(effect)
  ↓
cleanupEffect()
  ↓
effect.run()(重新执行)

这是 Vue3 响应式系统最重要的流程图之一。


🎯 四、真实示例:自动依赖切换

scss 复制代码
const flag = ref(true)
const a = ref(1)
const b = ref(2)

watchEffect(() => {
  console.log(flag.value ? a.value : b.value)
})

执行顺序:

  1. 初次执行 → 访问 a → 收集 a 的依赖
  2. flag 改成 false,重新执行 → 清理 a 依赖 → 收集 b 依赖
  3. a.value++ → 不会触发 effect
  4. b.value++ → 会触发 effect

问到这里绝对是高分。


🎯 五、常见面试官追问(附答法)


❓1:watchEffect 为什么能"自动知道依赖"?

因为执行回调时,Vue 会:

  • 设置 activeEffect
  • 每次读取响应式数据都会调用 track,自动把 activeEffect 存进去

换句话讲:

effect 的执行 = 顺便收集依赖


❓2:watchEffect 和 computed 的内部关系是什么?

回答要重点强调:

  • 二者都是 ReactiveEffect
  • computed 是 lazy effect(只有访问 value 才执行)
  • watchEffect 是 active effect(立即执行)

❓3:为什么 watchEffect 有 cleanup?

因为执行完一次后,下一次依赖可能改变

例如条件分支:

perl 复制代码
if (flag) use a
else use b

没有 cleanup 会导致:

  • 依赖污染
  • 不必要的触发
  • 内存泄漏

❓4:watchEffect 会不会死循环?

会,如果你这样写:

scss 复制代码
watchEffect(() => {
  obj.count++
})

因为执行 effect 会再次改数据,又触发 effect。

避免方法:

  • 使用 watch
  • 在回调内部不要直接修改依赖源

🎯 六、金牌总结(面试官非常喜欢听)

watchEffect = 自动依赖追踪 + 自动清理 + scheduler 调度执行。

执行时会访问哪些数据,就会被自动收集为依赖;依赖变化后重新执行 effect。

核心是 ReactiveEffect + track/trigger 机制,是 Vue3 响应式系统最重要的组成部分之一。

相关推荐
前端一课1 小时前
【vue高频面试题】第 21 题:Vue3 中的 Slot(插槽)— 基础、原理、使用场景、面试必问点
前端·面试
前端一课1 小时前
第 24 题:Vue3 的组件通信方式(props / emit / v-model / provide-inject / expose / eventBus
前端·面试
前端一课1 小时前
第 22 题:Teleport 的作用、原理和使用场景
前端·面试
前端一课1 小时前
第 29 题:Vue3 的 Teleport 原理(跨层级 DOM 挂载技术)
前端·面试
前端一课1 小时前
第 256 题:Vue3 的异步组件(defineAsyncComponent)+ Suspense 原理与面试高频点
前端·面试
前端一课1 小时前
第 27 题:Vue3 + TS 类型推断(Props 类型推导、Emit 类型推导、Setup 返回值类型)
前端·面试
是罐装可乐1 小时前
前端架构知识体系:通过发布-订阅者模式解耦路由和请求
前端·架构·vue·路由
杀死那个蝈坦1 小时前
监听 Canal
java·前端·eclipse·kotlin·bootstrap·html·lua
普贤莲花1 小时前
小米面试总结20251202
面试·职场和发展