发布时间:2026/7/22 22:25:39
《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/7/22 22:25:39

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:39

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

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

2026/7/22 22:25:39

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

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

2026/7/23 0:56:13

Java 继承②

目录1. super 关键字2. super 和 this3. this和super访问时的内存布局1. super 关键字 在父类成员变量和子类成员变量名字相同的情况下,为了在子类中访问父类的成员。 super关键字的用法 : super.父类成员变量 class Person{String name;int age 10;public void …

2026/7/23 0:56:13

Kafka与Zookeeper集群部署实战指南

1. Kafka与Zookeeper集群部署核心解析Kafka作为分布式消息系统的标杆,其高吞吐、低延迟的特性使其成为现代大数据架构的核心组件。而Zookeeper作为Kafka的"中枢神经系统",负责维护集群元数据、选举控制器节点以及监控Broker状态。这套组合在金…

2026/7/23 0:56:13

非接触式便携微型生理变异性监测设备的技术现状

1. 执行摘要 本报告旨在系统性地回答一个核心问题:在当前的消费级与医疗级市场中,是否存在已通过权威认证、能够准确可靠地检测心率、呼吸频率及其变异性(HRV/RRV)的非接触式、便携式、微型化生理监测设备。若不存在,本…

2026/7/23 0:56:13

影刀RPA 能源管理自动化:用电负荷曲线与节能分析

影刀RPA 能源管理自动化:用电负荷曲线与节能分析 作者:林焱 | 分类:影刀RPA新手教程 | 难度:★★ 什么情况用 企业能源管理需要跟踪用电数据:峰谷平电量分布、各车间用电对比、变压器负载率。电力数据通常在供电局平台…

2026/7/23 0:56:13

影刀RPA 网页验证码的识别与绕过:滑块拼图点选的自动化方案

影刀RPA 网页验证码的识别与绕过:滑块拼图点选的自动化方案 验证码是RPA的天敌——它被设计出来就是为了挡自动化的。 本文不讲"暴力破解",讲的是在合法业务场景下(你自己的系统、你授权的账号),怎么处理验…

2026/7/23 0:51:12

北京市专精特新中小企业认定常见问题解答

一、已获得科技和创新型中小企业称号,截至上年末从事特定细分市场时间达3年以上,如何理解?答:申请企业需为有效期内的创新型中小企业或科技型中小企业,两者满足其一。细分市场判断的时间是截至上年末(即202…

2026/7/22 9:29:13

Unity与Python本地通信:基于Flask的跨语言数据交换实战

1. 项目概述:为什么我们需要一个本地通信服务器?在游戏开发、数字孪生、仿真训练等众多领域,Unity作为强大的实时3D内容创作平台,其核心逻辑通常由C#驱动。然而,当我们需要进行复杂的数据分析、机器学习推理、科学计算…

2026/7/23 0:01:10

Chitchatter完整指南:免费开源的终极点对点安全聊天工具

Chitchatter完整指南:免费开源的终极点对点安全聊天工具 【免费下载链接】chitchatter Secure peer-to-peer chat that is serverless, decentralized, and ephemeral 项目地址: https://gitcode.com/gh_mirrors/ch/chitchatter Chitchatter是一款革命性的安…

2026/7/22 21:00:12

3个高效策略:快速掌握Axure中文界面配置

3个高效策略:快速掌握Axure中文界面配置 【免费下载链接】axure-cn Chinese language file for Axure RP. Axure RP 简体中文语言包。支持 Axure 11、10、9。不定期更新。 项目地址: https://gitcode.com/gh_mirrors/ax/axure-cn 还在为Axure RP的英文界面感…