vue响应式系统通过运行时动态依赖追踪与异步副作用触发实现精准更新:读取时用weakmap/map/set三层结构记录依赖,修改时调度去重执行,避免内存泄漏与无效更新。

Vue.js 的响应式系统靠“依赖追踪”和“副作用触发”形成闭环:数据被读时记下谁在用它,被改时通知这些使用者重新执行。这不是静态监听,而是运行时动态建立、精准匹配的关系。
依赖追踪:只在需要时登记关系
依赖不会预先注册,只在副作用函数(如组件渲染、watchEffect、computed)执行过程中,访问响应式数据时才发生:
- 访问 state.count 触发 get 拦截器 → 检查当前是否有活跃的 activeEffect
- 有则调用 track(target, 'count', activeEffect),把该 effect 加入 count 对应的依赖集合
- 条件分支中未执行的代码(如 v-if="false" 内的读取)不收集,解构后丢失代理(const { count } = reactive(...))也不参与追踪
副作用触发:改数据时不直接执行,而是调度重跑
赋值或调用变更方法(如 push、delete)会触发 trigger,但它只负责“通知”,不立即执行:
- 根据操作类型(set、delete、clear)和 key 查找对应依赖集合
- 将匹配的 effect 加入异步队列(微任务),同一 tick 内自动去重,避免重复更新
- 真正执行由调度器控制:默认走 queueJob;也可自定义 scheduler,比如延迟到宏任务或防抖
依赖存哪?为什么不会内存泄漏?
依赖不挂在原始对象上,而存在三层结构的映射表中:
- targetMap:WeakMap,键是响应式对象,值是它的属性依赖图
- depsMap:Map,键是属性名(如 'count'),值是依赖它的 effect 集合
- dep:Set,存具体要通知的 effect 函数
- WeakMap 设计让目标对象被销毁时,相关依赖记录自动回收,避免内存泄漏
常见失效原因:不是数据没变,而是关系没建上
多数“响应不生效”问题源于依赖未正确建立:
- 用普通对象代替 reactive 或 ref,或用 shallowRef/markRaw 阻断了深层响应
- 首次读取时条件为假(如 if (loading) console.log(state.data)),后续条件为真时依赖已错过收集时机
- 在 effect 外部直接修改原始对象(绕过 proxy),或数组索引赋值未触发 length 变更(需用 Vue.set 或 splice)
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











