《Vue3 从入门到大神35篇》Vue3 源码详解(五):effect 依赖收集原理——track 与 trigger 是如何工作的?

发布时间:2026/9/13 14:56:27

《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 源码分析——懒计算与缓存机制的实现》
延伸阅读

更多相关文章

2026/9/13 5:08:07

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

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

2026/9/13 14:55:45

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

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

2026/9/11 12:32:10

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

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

2026/9/13 14:52:45

可食用程序技术:从二维码到生物编码的创新应用

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

2026/9/13 14:52:45

GD32F103手搓FreeRTOS内核:从启动文件到上下文切换全链路解析

1. 项目概述:这不是“点灯”,而是一次嵌入式系统认知的彻底重装“点灯大师进阶,从手搓操作系统开始(10)”——这个标题乍看像极了嵌入式新手教程里常见的“点亮LED”彩蛋,但括号里的“(10&#…

2026/9/13 14:52:45

gRPC-Go 客户端创建反模式与 RPC 错误处理最佳实践

gRPC-Go 客户端创建反模式与 RPC 错误处理最佳实践 【免费下载链接】grpc-go The Go language implementation of gRPC. HTTP/2 based RPC 项目地址: https://gitcode.com/GitHub_Trending/gr/grpc-go 本文以 grpc-go 仓库的 anti-patterns.md 为核心,系统梳…

2026/9/13 0:01:16

拯救者Y7000黑屏故障排查与维修实战指南

1. 项目概述:一台黑屏的拯救者Y7000,到底卡在哪一步? 联想拯救者Y7000系列笔记本,从2018年第一代搭载i5-8300H开始,到后来的i7-9750H、i7-10750H、i5-11400H,再到2023年款的R7-7840HS,它始终是学…

2026/9/13 0:01:16

拯救者Y7000黑屏故障排查与维修实战指南

1. 项目概述:一台黑屏的拯救者Y7000,到底卡在哪一步? 联想拯救者Y7000系列笔记本,从2018年第一代搭载i5-8300H开始,到后来的i7-9750H、i7-10750H、i5-11400H,再到2023年款的R7-7840HS,它始终是学…

2026/9/12 6:29:36

USB Type-C PCB布局分区设计:电源、高速信号与PD协议全攻略

做硬件这行,Type-C接口算是典型的“看着简单,做起来全坑”的东西。光引脚就24个,高低速信号、电源、控制线全部塞在一个小小的连接器里,如果PCB布局不做规划,打样回来基本就是“插上没反应”、“高速掉线”、“静电一打…

2026/9/12 14:32:17

系统编程学习原型如何补齐稳定性边界

系统编程学习原型如何补齐稳定性边界预算有限时&#xff0c;我先优化明显多余的复制&#xff0c;而不是猜测性地换容器。用借用传递只读数据通常就能减少分配&#xff1a; fn parse(line: &str) -> Result<Item, Error> { /* ... */ }用基准确认热点确实在分配&am…

2026/9/13 11:18:28

雨花区哪家财务公司代理记账比较好?

在雨花区&#xff0c;企业处理财税事务常常面临诸多挑战&#xff0c;选择一家靠谱的财务公司至关重要。湖南巨勤财务管理咨询有限公司就是本地正规实体财税服务机构&#xff0c;深耕本地工商财税行业多年&#xff0c;熟悉当地工商局、税务局最新政策与申报流程。主营公司注册、…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
咨询二维码