《Vue3 从入门到大神35篇》Vue3 源码详解(五):effect 依赖收集原理——track 与 trigger 是如何工作的?
本篇关键词effect、effectStack、Dep、cleanup、依赖清理、嵌套 effect、调度雏形难度等级⭐⭐⭐⭐⭐工程价值这是整个 Vue3 响应式系统的“心脏”。读懂这一篇你不仅能彻底理解 computed、watch、组件更新的本质还能从根本上规避“依赖残留”“无限循环”等高级 Bug。一、开篇为什么 effect 是响应式的灵魂在 Vue3 中一切副作用都源于effectcomputed→ 特殊的effectwatch/watchEffect→ 包装后的effect组件渲染 → 一个巨大的componentEffect一句话定义effect 是“可被重新执行的副作用函数”是数据与视图之间的桥梁。二、回顾依赖收集的三角关系我们在第三篇手写过这个结构target → key → dep → effect但在 Vue3 源码中这个关系更加严谨WeakMaptarget, Mapkey, Dep Dep SetReactiveEffect而effect本身并不只是一个函数而是一个对象。三、ReactiveEffecteffect 的真实身份在packages/reactivity/src/effect.ts中核心类是class ReactiveEffectT any { active true deps: Dep[] [] // 记录我依赖了哪些 Dep parent: ReactiveEffect | null null constructor( public fn: () T, public scheduler?: EffectScheduler ) {} run() { // 核心逻辑 } }为什么 deps 是一个数组因为一个effect可能依赖多个响应式属性effect(() { console.log(state.a state.b) })deps同时包含a和b对应的Dep。四、effectStack解决嵌套 effect 的关键1️⃣ 为什么会有嵌套 effecteffect(() { console.log(parent) effect(() { console.log(child) }) })Vue 组件渲染本身就是一个嵌套结构App.vue ├─ CompA.vue │ └─ CompB.vue每一层渲染都是一个effect。2️⃣ effectStack 的实现let activeEffect: ReactiveEffect | null null const effectStack: ReactiveEffect[] [] class ReactiveEffect { run() { if (!this.active) return this.fn() if (!effectStack.includes(this)) { try { effectStack.push((activeEffect this)) return this.fn() } finally { effectStack.pop() activeEffect effectStack[effectStack.length - 1] || null } } } }✅关键点activeEffect永远指向“当前正在执行的 effect”finally确保即使报错也能恢复栈这就是 Vue3 能正确处理嵌套渲染的根本原因。五、track依赖收集的完整流程1️⃣ track 的触发时机// reactive get 拦截中 get(target, key) { track(target, TrackOpTypes.GET, key) return Reflect.get(...) }2️⃣ track 源码精读export function track(target: object, type: TrackOpTypes, key: unknown) { if (!activeEffect) return let depsMap targetMap.get(target) if (!depsMap) { targetMap.set(target, (depsMap new Map())) } let dep depsMap.get(key) if (!dep) { depsMap.set(key, (dep new Set())) } if (!dep.has(activeEffect)) { dep.add(activeEffect) activeEffect.deps.push(dep) } }重点拆解✅双向记录Dep记录effecteffect.deps记录Dep为什么要双向记录为了后续清理依赖。六、trigger依赖触发的完整流程1️⃣ trigger 的触发时机set(target, key, value) { trigger(target, TriggerOpTypes.SET, key) return Reflect.set(...) }2️⃣ trigger 源码精读简化版export function trigger(target: object, type: TriggerOpTypes, key: unknown) { const depsMap targetMap.get(target) if (!depsMap) return const effects new SetReactiveEffect() const add (effectsToAdd?: Dep) { effectsToAdd?.forEach(effect effects.add(effect)) } add(depsMap.get(key)) effects.forEach(effect { if (effect.scheduler) { effect.scheduler() } else { effect.run() } }) }✅核心逻辑找到 key 对应的所有 effect如果有 scheduler → 交给调度器否则 → 立即执行这就是 computed、watch、组件更新的统一入口。七、依赖清理cleanupVue3 相比 Vue2 的巨大进步1️⃣ Vue2 的经典 Bugif (state.show) { console.log(state.msg) }当state.show从true→falseVue2仍然会收集state.msg的依赖造成依赖残留2️⃣ Vue3 的解决方案cleanupEffectfunction cleanupEffect(effect: ReactiveEffect) { const { deps } effect for (let i 0; i deps.length; i) { deps[i].delete(effect) } effect.deps.length 0 }在effect.run()之前调用run() { cleanupEffect(this) effectStack.push((activeEffect this)) try { return this.fn() } finally { effectStack.pop() activeEffect effectStack[effectStack.length - 1] || null } }✅效果每次执行前清空旧依赖执行后重新收集新依赖完美适配if / switch / 三元表达式这是 Vue3 响应式系统“自愈能力”的来源。八、调度器雏形为什么 Vue3 不会频繁更新1️⃣ 问题场景state.count state.count state.count你不希望触发三次更新。2️⃣ scheduler 的作用class ReactiveEffect { constructor( public fn: () T, public scheduler?: EffectScheduler ) {} } run() { if (this.scheduler) { this.scheduler(this) } else { this.fn() } }组件渲染 effect 的 scheduler将更新推入微任务队列合并多次更新实现nextTick下一篇讲 watch / computed 时会深入九、调试实战用断点验证全流程在 VSCode 中依次在这些位置打断点packages/reactivity/src/effect.ts ├── ReactiveEffect.run() ├── track() ├── trigger() └── cleanupEffect()然后运行const state reactive({ ok: true, text: hello }) effect(() { console.log(state.ok ? state.text : no) }) state.ok false state.text world观察cleanupEffect是否被调用effect.deps如何变化trigger中effects的数量你会发现Vue3 的依赖系统是“动态重构”的。十、面试追问为什么 effect.deps 是数组而不是 Set因为同一个 Dep 可能被多个 key 共享数组更利于 cleanup。Vue3 如何避免死循环activeEffect在执行期间不会被重复加入 Dep。computed 和 effect 的区别computed 是懒执行 缓存 脏检查的 effect。为什么 cleanup 要在 run 之前防止旧依赖污染新依赖避免内存泄漏。十一、本篇总结✅effect不是函数而是对象✅effectStack解决嵌套执行问题✅track双向记录为 cleanup 做准备✅cleanupEffect是 Vue3 响应式“自愈”的关键✅scheduler是性能优化的入口一句话升华Vue3 的响应式系统不是“收集一次依赖就结束”而是每一次执行都是一次依赖的重建。十二、下一篇预告《Vue3 源码详解六computed 源码分析——懒计算与缓存机制的实现》

相关新闻

TAICHI-flet终极指南:如何快速解决99%的常见问题

TAICHI-flet终极指南:如何快速解决99%的常见问题

TAICHI-flet终极指南:如何快速解决99%的常见问题 【免费下载链接】TAICHI-flet 基于flet的一款windows桌面应用,实现了浏览图片、音乐、小说、漫画、各种资源的功能。 项目地址: https://gitcode.com/GitHub_Trending/ta/TAICHI-flet TAICHI-flet…

2026/7/22 22:25:12 阅读更多 →
健康管理:跳出认知误区看本质

健康管理:跳出认知误区看本质

健康管理:跳出认知误区看本质第一,拆解本质,定底层逻辑 健康管理的本质并非大众认知中的“标准化养生套餐”,而是基于个体信息的系统性健康维护过程。根据行业普遍认知,影响健康的因素构成复杂系统:遗传因素…

2026/7/22 22:25:12 阅读更多 →
Tinygrad终极指南:轻量级深度学习框架的10个核心优势

Tinygrad终极指南:轻量级深度学习框架的10个核心优势

Tinygrad终极指南:轻量级深度学习框架的10个核心优势 【免费下载链接】tinygrad You like pytorch? You like micrograd? You love tinygrad! ❤️ 项目地址: https://gitcode.com/GitHub_Trending/tiny/tinygrad 你知道吗?在深度学习框架的世…

2026/7/22 22:25:12 阅读更多 →

最新新闻

USB设备中断服务例程(ISR)设计:从原理到实战的嵌入式开发指南

USB设备中断服务例程(ISR)设计:从原理到实战的嵌入式开发指南

1. USB设备中断服务例程(ISR)的核心价值与设计挑战在嵌入式系统里做USB设备开发,中断服务例程(ISR)的设计绝对是整个固件架构的“心脏”。它直接决定了你的设备能否稳定、高效地与主机“对话”。很多刚接触USB协议栈的…

2026/7/22 23:54:24 阅读更多 →
提升.NET API安全性:Boxed.AspNetCore.Swagger认证授权最佳实践

提升.NET API安全性:Boxed.AspNetCore.Swagger认证授权最佳实践

提升.NET API安全性:Boxed.AspNetCore.Swagger认证授权最佳实践 【免费下载链接】Framework .NET Core Extensions and Helper NuGet packages. 项目地址: https://gitcode.com/gh_mirrors/framework8/Framework 在构建现代.NET API时,确保认证授…

2026/7/22 23:54:24 阅读更多 →
提示词权重分配全解析,深度解读Top 3企业级排序模型与AB测试验证数据

提示词权重分配全解析,深度解读Top 3企业级排序模型与AB测试验证数据

更多请点击: https://intelliparadigm.com 第一章:提示词权重分配全解析,深度解读Top 3企业级排序模型与AB测试验证数据 提示词权重分配是大模型应用落地的核心调控杠杆,直接影响生成质量、业务转化率与推理成本。企业级场景中&a…

2026/7/22 23:54:24 阅读更多 →
市场营销专业学生怎么做数据分析项目?

市场营销专业学生怎么做数据分析项目?

市场营销专业的学生想做数据分析项目,但常常不知道从哪下手。其实方向很明确——用户画像、投放复盘、竞品分析、品牌数据、活动效果分析,这五个场景就是最好的切入点。先看一组数据。字节跳动2026届校招中AI相关岗位占比65%以上,较2025届提升…

2026/7/22 23:54:24 阅读更多 →
模型漂移预警失效,搜索CTR骤降47%?——从日志到归因的AI搜索分析闭环(含可落地的Python诊断脚本)

模型漂移预警失效,搜索CTR骤降47%?——从日志到归因的AI搜索分析闭环(含可落地的Python诊断脚本)

更多请点击: https://intelliparadigm.com 第一章:模型漂移预警失效,搜索CTR骤降47%?——从日志到归因的AI搜索分析闭环(含可落地的Python诊断脚本) 当线上A/B测试未触发任何告警,而核心指标CT…

2026/7/22 23:54:24 阅读更多 →
从0到1掌握MultipleStatusView:新手友好的Android多状态视图组件教程

从0到1掌握MultipleStatusView:新手友好的Android多状态视图组件教程

从0到1掌握MultipleStatusView:新手友好的Android多状态视图组件教程 【免费下载链接】MultipleStatusView 一个支持多种状态的自定义View,可以方便的切换到:加载中视图、错误视图、空数据视图、网络异常视图、内容视图。 项目地址: https://gitcode.c…

2026/7/22 23:53:24 阅读更多 →

日新闻

TI DSP系统配置模块SYSCFG详解:中断机制与主设备优先级配置实战

TI DSP系统配置模块SYSCFG详解:中断机制与主设备优先级配置实战

1. 项目概述与SYSCFG模块的核心价值在嵌入式系统,尤其是像TI C6000系列这样的高性能DSP开发中,我们常常会与芯片手册里那些密密麻麻的寄存器打交道。很多开发者可能更关注算法实现、内存优化或者外设驱动,但对于一个稳定、高效的系统而言&…

2026/7/22 0:00:26 阅读更多 →
微信Server酱:高到达率的应急通知方案实践

微信Server酱:高到达率的应急通知方案实践

1. 为什么我们需要"最次"的通知方案? 在数字化协作环境中,消息通知系统的重要性不言而喻明。但现实情况是,企业级通知方案往往需要复杂的API对接(如企业微信、钉钉、飞书),个人开发者的小项目又经…

2026/7/22 0:00:26 阅读更多 →
甲方要的“简洁“PPT,到底是简洁还是省事?

甲方要的“简洁“PPT,到底是简洁还是省事?

甲方说"简洁一点",乙方听到的是"少做几页"。甲方说"不要太复杂",乙方理解成"别放图表了"。结果交过去,甲方说"我说的简洁不是这个意思"。"简洁"这个词在PPT语境里,是…

2026/7/22 0:00:26 阅读更多 →

周新闻

Go语言静态资源打包方案对比与实践指南

Go语言静态资源打包方案对比与实践指南

1. 项目背景与核心需求在Go语言开发中,我们经常需要处理静态资源文件的打包问题。无论是Web应用的模板文件、前端资源,还是配置文件、证书等,都需要随程序一起分发。传统做法是将这些文件与编译后的二进制文件放在同一目录下,但这…

2026/7/22 8:58:19 阅读更多 →
Go语言实现高性能LDAP认证服务的架构与实践

Go语言实现高性能LDAP认证服务的架构与实践

1. 项目背景与核心价值LDAP(轻量级目录访问协议)作为企业级身份认证的黄金标准,已经服务了超过80%的财富500强公司。我在金融科技领域实施统一认证体系时,发现传统Java方案存在启动慢、内存占用高等痛点。而Go语言凭借其协程并发模…

2026/7/22 19:43:43 阅读更多 →
【AI面试官实战指南】:用ChatGPT模拟10类高频技术岗面试,3天提升应答精准度92%

【AI面试官实战指南】:用ChatGPT模拟10类高频技术岗面试,3天提升应答精准度92%

更多请点击: https://intelliparadigm.com 第一章:AI面试官实战指南的核心价值与适用场景 AI面试官并非替代人类HR的“黑箱工具”,而是以可解释、可审计、可迭代的方式,赋能招聘全链路的关键基础设施。其核心价值在于将主观经验沉…

2026/7/22 12:54:44 阅读更多 →

月新闻