尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
《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 的本质不是“监听数据”而是“在正确的时机以正确的方式执行正确的副作用”。
RELATED

相关推荐

专业AI机构技术架构与大模型实战应用解析

专业AI机构技术架构与大模型实战应用解析

1. 专业AI机构的技术架构解析 当我们在讨论专业人工智能机构时,核心在于理解他们如何将前沿技术转化为实际生产力。这类机构通常具备三大核心能力:技术整合能力、场景落地能力和持续迭代能力。 以我参与过的多个AI项目经验来看,一个成熟的AI…

📅 2026/9/14 20:39:15
AI显微图像增强技术:原理、应用与优化

AI显微图像增强技术:原理、应用与优化

1. AI显微图像增强技术概述 在生物医学和材料科学研究领域,显微镜图像质量直接影响着后续分析的准确性。传统显微图像常面临低对比度、高噪声和分辨率不足等问题,这些问题在活细胞成像、低光照条件或快速动态观察时尤为突出。AI显微图像增强技术通过深度…

📅 2026/8/10 11:48:40
AI系统安全治理:自主智能体行为约束与防控机制

AI系统安全治理:自主智能体行为约束与防控机制

1. 论文核心议题解析这篇修订版论文聚焦于AI系统安全治理的前沿领域,探讨了自主智能体可能产生的非预期行为及其防控机制。不同于常见的AI伦理讨论,本文从技术实现层面切入,提出了可落地的安全框架设计思路。在自动驾驶、医疗诊断等关键领域&…

📅 2026/8/1 17:27:04
MORE NEWS

更多资讯

📰

SAP SD定价过程详解:从条件类型到存取顺序的配置与排错

简介:这是一份深入讲解SAP SD模块定价逻辑的Word文档,适合SAP SD顾问、实施人员及需要理解销售订单价格计算原理的ERP从业者。文档从R/3系统如何根据物料金额计算净价值与总金额切入,完整梳理了定价过程的确定框架、客户定价过程与单据定价过…

📰

从Word结构到自动评分:Python解析中文版NOS量表docx实践

简介:一份面向临床科研人员、循证医学研究者及系统评价/Meta分析初学者的文献质量评价工具,内容为纽卡斯尔-渥太华量表(NOS)中文版。量表覆盖病例对照研究与队列研究两大类设计,条目涉及病例定义与代表性、对照选择、暴…

📰

用JUnit 5构建可修改的Java基础题库:从参数化测试到动态加载

简介:面向Java入门学习者的一套基础练习题及答案文档,聚焦main方法定义、JVM执行特点、Java语言特性、符号与表达式、基本数据类型、运算符、控制结构以及异常处理等核心考点,同时涵盖简单Java程序调试、表达式取值、隐式与显式类型转换等常见…

📰

SDK、API、Library别再搞混:从概念到报错排查一篇讲透

做开发这几年,被问得最多的一个基础问题不是算法,也不是架构,而是:“SDK 到底是什么?它和 API、Library 有啥区别?”尤其是刚转行入门的同学,看文档时经常发现同一行代码里既出现 SDK&#xff0…

📰

Altium Designer 2024安装教程:系统配置、组件选择与故障排查指南

1. 为什么还要写一份2024版的安装指南Altium Designer 2024 的安装包体积已经逼近 6GB,安装完成后占用的磁盘空间轻松超过 15GB,再加上元件库、仿真模型和各类插件,整套环境搭下来对系统资源的消耗相当可观。很多刚接触这个工具的朋友&#x…

📰

Apache Spark SQL Pipe Syntax 完全指南:使用 `|>` 链式组合查询操作符

Apache Spark SQL Pipe Syntax 完全指南:使用 |> 链式组合查询操作符 【免费下载链接】spark Apache Spark - A unified analytics engine for large-scale data processing 项目地址: https://gitcode.com/gh_mirrors/sp/spark SQL Pipe Syntax 是 Apach…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬