第 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 响应式系统最重要的组成部分之一。

相关推荐
子兮曰19 小时前
OpenClaw入门:从零开始搭建你的私有化AI助手
前端·架构·github
吴仰晖19 小时前
使用github copliot chat的源码学习之Chromium Compositor
前端
1024小神19 小时前
github发布pages的几种状态记录
前端
不像程序员的程序媛21 小时前
Nginx日志切分
服务器·前端·nginx
北原_春希21 小时前
如何在Vue3项目中引入并使用Echarts图表
前端·javascript·echarts
尽意啊21 小时前
echarts树图动态添加子节点
前端·javascript·echarts
吃面必吃蒜21 小时前
echarts 极坐标柱状图 如何定义柱子颜色
前端·javascript·echarts
O_oStayPositive21 小时前
Vue3使用ECharts
前端·javascript·echarts
竹秋…21 小时前
echarts自定义tooltip中的内容
前端·javascript·echarts
宝贝露.21 小时前
Axure引入Echarts图无法正常显示问题
前端·javascript·echarts