track 是 vue 在读取响应式数据时主动登记“当前 effect 依赖哪个对象的哪个属性”的过程,通过 proxy 的 get 拦截器触发 track(),利用 weakmap→map→set 三级结构存储依赖关系,并支持对非响应式对象手动追踪。

“依赖收集”中的 Track 过程,本质是 Vue 在读取响应式数据时,**主动记录“当前正在执行的副作用函数(effect)依赖了哪个对象的哪个属性”**。它不是被动监听,而是由 Proxy 的 get 拦截器触发、由 track() 函数完成的一次明确映射登记。
Track 是怎么工作的?
Vue 内部维护一个三级结构:WeakMap → Map → Set:
- WeakMap:以响应式对象(target)为 key,存储它所有被追踪的属性关系
- Map:以属性名(key)为 key,对应一个依赖集合
- Set:存放所有依赖该属性的 effect(即副作用函数)
当你在 effect 中访问 state.count,Proxy 的 get 就会调用 track(state, 'count'),把当前 activeEffect 加入 state.count 对应的依赖集合中——这就完成了“谁用了我”的登记。
为什么需要手动调用 track()?
默认情况下,只有在响应式对象上读取属性才会自动 track。但有些场景下,你操作的是普通对象、缓存、外部状态,或者想绕过响应式代理做精细控制。这时就得手动介入:
- 让 effect 依赖一个非响应式值(比如
cache.data) - 在深克隆、序列化等批量读取时不触发无效依赖
- 调试时显式标记某次读取是否应纳入响应链
手动追踪数据流转的实战写法
假设你有一个不响应式的本地缓存,又希望组件在缓存更新后重新渲染:
```jsimport { effect, track, trigger, TriggerOpTypes, TrackOpTypes } from 'vue/reactivity'
const cache = { data: null } // 普通对象,无响应性
// 声明一个副作用:只要 cache.data 被 track 过,就把它当作依赖
effect(() => {
track(cache, TrackOpTypes.GET, 'data') // 手动声明依赖
console.log('缓存值:', cache.data)
})
// 外部更新缓存后,主动通知变化
function updateCache(newVal) {
cache.data = newVal
trigger(cache, TriggerOpTypes.SET, 'data', newVal, null)
}
```
这段代码的关键在于:track() 让 Vue “记住”这个 effect 关心 cache.data;trigger() 则在值变后“唤醒”它——即使 cache 本身不是 reactive,也能实现响应式联动。
常见误用提醒
-
track()必须在 effect 执行期间调用,否则activeEffect为空,无效 - 不要对同一个 target/key 多次重复 track,Set 自动去重,但逻辑易混乱
- 手动 track + trigger 适合明确边界的小范围控制,别用来替代 reactive/ref










