《Vue3 从入门到大神37篇》Vue3 源码详解(七):watch 与 watchEffect 源码对比——副作用是如何被追踪的? 本篇关键词watch、watchEffect、job、scheduler、flush、pre / post / sync、cleanup难度等级⭐⭐⭐⭐⭐工程价值watch 是 Vue3 中最复杂的响应式 API。读懂它你才能真正理解“副作用调度”“异步更新”“竞态处理”这些高阶工程问题。一、开篇为什么 watch 比 computed 复杂很多同学觉得“watch 不就是监听数据变化吗”但真实情况是为什么watchEffect会自动收集依赖为什么watch需要明确指定 source为什么flush: post能拿到更新后的 DOM为什么 watch 能拿到新旧值而 watchEffect 不行为什么 watch 能防止“竞态问题”答案不在 API 层面而在调度系统Scheduler中。二、一句话区分 watch 和 watchEffectAPI本质watchEffect立即执行的副作用 自动依赖收集​watch显式数据源 惰性执行 新旧值对比 调度控制​watch 是 watchEffect 的“增强版”。三、watchEffect 源码解析自动依赖收集的真相1️⃣ 使用方式watchEffect(() { console.log(state.count) })特点✅ 立即执行✅ 自动收集依赖❌ 拿不到旧值❌ 无法精确控制触发时机2️⃣ watchEffect 的核心实现简化版源码位置packages/runtime-core/src/apiWatch.tsfunction watchEffect( effectFn, options {} ) { return doWatch(effectFn, null, options) }所有魔法都在doWatch里。3️⃣ watchEffect 的 effect 创建const effect new ReactiveEffect(effectFn) effect.scheduler () { if (flush sync) { job() } else { queueJob(job) } }关键点watchEffect内部也是一个ReactiveEffect通过scheduler接入 Vue 的异步调度系统默认是异步执行flush: pre4️⃣ 为什么 watchEffect 能自动收集依赖effect.run()执行effectFn时读取响应式数据触发track建立依赖关系和普通 effect 没有任何区别。四、watch 源码解析精确控制的艺术1️⃣ watch 的使用方式watch( () state.count, (newVal, oldVal) { console.log(newVal, oldVal) }, { flush: post, immediate: true } )特点✅ 明确指定 source✅ 拿到新旧值✅ 支持immediate✅ 支持deep✅ 支持flush控制2️⃣ watch 的核心实现简化版function watch(source, cb, options) { return doWatch(source, cb, options) }没错watch 和 watchEffect 共用同一个doWatch。区别在于有没有 callback。五、doWatchwatch 系统的真正大脑1️⃣ source 的三种形态Vue3 支持三种 watch source// 1️⃣ getter watch(() state.count, cb) // 2️⃣ ref watch(countRef, cb) // 3️⃣ reactive watch(state, cb, { deep: true })源码中统一处理let getter: () any if (isRef(source)) { getter () source.value } else if (isReactive(source)) { getter () source deep true } else if (isFunction(source)) { getter source }2️⃣ jobwatch 回调的执行单元const job () { if (cb) { const newValue effect.run() if (deep || hasChanged(newValue, oldValue)) { cb(newValue, oldValue) oldValue newValue } } else { effect.run() } }新旧值对比发生在 job 中而不是 trigger 中。3️⃣ 为什么 watch 能拿到旧值核心秘密let oldValue initialValue oldValue newValueeffect.run()得到新值与缓存的旧值对比符合条件才执行回调✅这就是 watch 能精准控制更新的原因。六、flush 机制pre / post / sync 的本质这是 watch 最难理解但工程价值最高的部分。1️⃣ flush: pre默认flush: pre执行时机组件更新前源码逻辑queuePreFlushCb(job)✅适合大多数业务逻辑2️⃣ flush: postflush: post执行时机组件更新后 → DOM 已更新源码逻辑queuePostRenderEffect(job, instance)✅适合操作 DOM、获取尺寸、Scroll 位置watch(source, cb, { flush: post }) onMounted(() { // 等价于 watch post })3️⃣ flush: syncflush: sync执行时机数据变化 → 立即执行源码逻辑job()⚠️慎用可能导致多次同步更新性能差。七、deep watch性能与精度的权衡1️⃣ 为什么 reactive 默认 deepwatch(state, cb)因为getter () source track(state, GET, ITERATE_KEY)一次性追踪整个对象。2️⃣ deep 的实现原理function traverse(value, seen new Set()) { if (!isObject(value) || seen.has(value)) return seen.add(value) for (const key in value) { traverse(value[key], seen) } return value }递归访问每一个属性强制触发 track。⚠️性能代价极高慎用 deep。八、cleanup解决竞态问题的关键1️⃣ 竞态问题示例watch(id, async (newId) { const res await fetchData(newId) data.value res })如果id快速变化请求 A 发出请求 B 发出B 先返回A 后返回 →数据错乱2️⃣ watch 的 cleanup 机制watch(id, async (newId, oldId, onCleanup) { let canceled false onCleanup(() { canceled true }) const res await fetchData(newId) if (!canceled) { data.value res } })源码本质const cleanup effect.onStop || (() {})每次重新执行前先执行上一次的 cleanup。✅这是前端“竞态控制”的标准解法。九、watchEffect vs watch源码级对比表维度watchEffectwatch依赖收集自动显式立即执行✅可选新旧值❌✅深度监听❌✅调度控制基础精细竞态处理❌✅适用场景副作用数据变化响应十、调试实战观察 watch 的调度过程在 VSCode 中打这些断点packages/runtime-core/src/apiWatch.ts ├── doWatch() ├── job() ├── queueJob() └── cleanup()测试代码watchEffect(() { console.log(effect, state.count) }) watch(() state.count, (n, o) { console.log(watch, n, o) }) state.count观察queueJob何时入队job何时执行DOM 更新前后顺序你会彻底理解 Vue3 的异步更新机制。十一、工程最佳实践✅ 优先使用 watchEffectwatchEffect(() { document.title state.title })✅ DOM 相关用 flush: postwatch(element, () { el.getBoundingClientRect() }, { flush: post })✅ 异步请求必用 cleanupwatch(id, (newId, _, onCleanup) { const abort new AbortController() onCleanup(() abort.abort()) fetch(url, { signal: abort.signal }) })❌ 避免 deep watch// ❌ 性能差 watch(state, cb, { deep: true }) // ✅ 精确监听 watch(() state.user.name, cb)十二、本篇总结✅ watch / watchEffect 共用doWatch✅ 调度系统queueJob是异步更新的核心✅ flush 控制执行时机pre / post / sync✅ cleanup 是解决竞态问题的关键✅ deep watch 有显著性能代价一句话升华watch 的本质不是“监听数据”而是“在正确的时机以正确的方式执行正确的副作用”。

相关新闻

最新新闻

SerenityOS 命令行选项解析指南:getopt 与 getopt_long 用法、返回值与底层实现

SerenityOS 命令行选项解析指南:getopt 与 getopt_long 用法、返回值与底层实现

SerenityOS 命令行选项解析指南:getopt 与 getopt_long 用法、返回值与底层实现 【免费下载链接】serenity The Serenity Operating System 🐞 项目地址: https://gitcode.com/GitHub_Trending/se/serenity 导读 本文以 getopt(3) 手册 为核心&a…

2026/10/3 16:42:15
轻量服务器还是ECS?大促云服务器选购与避坑实战指南

轻量服务器还是ECS?大促云服务器选购与避坑实战指南

每年大促节点,群里永远有人在问同一个问题:“38元的轻量服务器到底怎么抢?为什么我每次点进去都是已售罄?68元直购和99元的ECS我到底选哪个?”作为一个常年帮团队和自己采购云服务器的老用户,我太清楚这种纠…

2026/10/3 16:42:30
为 AI 代理的 Review 动作编写 Cedar 审批门控策略:review-agent-governance 策略编写实战指南

为 AI 代理的 Review 动作编写 Cedar 审批门控策略:review-agent-governance 策略编写实战指南

为 AI 代理的 Review 动作编写 Cedar 审批门控策略:review-agent-governance 策略编写实战指南 【免费下载链接】agents Multi-harness agentic plugin marketplace for Claude Code, Codex, Cursor, OpenCode, GitHub Copilot, and Google Antigravity 项目地址:…

2026/10/3 16:42:22
PaddleOCR 手写数学公式识别算法 CAN 实战指南:Counting-Aware Network 训练、评估与推理部署

PaddleOCR 手写数学公式识别算法 CAN 实战指南:Counting-Aware Network 训练、评估与推理部署

PaddleOCR 手写数学公式识别算法 CAN 实战指南:Counting-Aware Network 训练、评估与推理部署 【免费下载链接】PaddleOCR Turn any PDF or image document into structured data for your AI. A powerful, lightweight OCR toolkit that bridges the gap between i…

2026/10/3 7:41:27
Spring源码解析:构造器注入的类型转换与候选匹配机制

Spring源码解析:构造器注入的类型转换与候选匹配机制

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/3 16:42:24
openai-agents-python 多模型接入指南:深入解析 AnyLLMModel 适配层与 any-llm 路由

openai-agents-python 多模型接入指南:深入解析 AnyLLMModel 适配层与 any-llm 路由

openai-agents-python 多模型接入指南:深入解析 AnyLLMModel 适配层与 any-llm 路由 【免费下载链接】openai-agents-python A lightweight, powerful framework for multi-agent workflows 项目地址: https://gitcode.com/GitHub_Trending/op/openai-agents-pyth…

2026/10/3 16:42:28

日新闻

周新闻

月新闻