依赖收集发生在读取响应式数据时,由活跃的副作用(如render、computed、watchEffect)触发getter或Proxy的get拦截器,自动将当前effect注册到对应属性的独立Dep中;变更后通过异步队列合并更新。

依赖收集不是预先登记,而是在数据被读取的瞬间动态发生——谁读了这个值,Vue 就立刻记下“谁在盯它”,等改的时候只喊这几位。
依赖收集发生在“读取时”,不是定义或赋值时
只有当某个响应式属性在活跃的副作用(比如组件 render、computed getter、watchEffect)中被访问,它的 getter 或 Proxy 的 get 拦截器才会触发依赖收集。全局 console.log(obj.name) 不会收集,v-if="false" 里的表达式也不会执行 getter,自然不收集。
- 模板中写 {{ count }} → 渲染 Watcher 执行,触发 count 的 getter → 收集
- computed: { fullName() { return this.first + ' ' + this.last } } → 首次求值时,first 和 last 的 getter 各自触发一次收集
- watch: { user.name() {} } → 内部新建一个 Watcher 去读 user.name → 完成收集
每个属性独享一个 Dep(依赖容器)
Vue 给每个响应式属性(或 ref 的 .value)背后配一个独立的 Dep 实例,本质是一个 Set,专门存依赖它的 Watcher 或 effect。对象嵌套越深,对应层级的属性就各自有各自的 Dep;数组的 length、push 等操作也会关联到对应 Dep。
将一个或多个视频上传至YouTube。当用户想“上传到YouTube”“发YouTube”“批量上传”“upload to YouTube”“post videos to YouTube”或发布到YouTube时使用此技能。
- user.name 和 user.age 各自拥有不同的 Dep,互不干扰
- 数组 arr 的 arr[0]、arr.length、arr.push 都可能触发不同 Dep 的 track
- 新增属性 obj.newKey = 'x' 不会自动收集,因为没走 getter;需用 Vue.set 或 reactive(obj).newKey = 'x'(Vue 3)才可补上
Watcher 是“观察者身份”,effect 是 Vue 3 的统一抽象
Vue 2 中,每个组件渲染函数、每个 computed、每个 watch 都对应一个 Watcher 实例;Vue 3 则统一为 effect 函数,所有响应式逻辑都基于 effect 构建。它们共同特点是:执行时把自己设为当前活跃 effect,读取响应式数据时,自动注册进对应 Dep。
立即学习“前端免费学习笔记(深入)”;
- 组件首次挂载 → 触发 render effect → 读取 data → 自动完成依赖登记
- computed 属性有自己的 effect,只订阅它真正用到的字段
- watch(fn, options) 底层也是 effect,但带 cleanup 和 deep 控制逻辑
收集完不等于马上更新,变化后走异步调度
setter 或 set 拦截器触发后,Vue 不立即执行更新,而是把对应 Dep 中的所有 effect 加入一个微任务队列(queueJob),同一 tick 内重复触发只保留最后一次,避免过度渲染。
- this.count = 1; this.count = 2; → 最终只执行一次 render effect
- 多个响应式字段同时变更,只要属于同一个组件,也合并为一次 DOM 更新
- 更新前会重新运行 effect,生成新虚拟 DOM,再与旧树 diff,精准打补丁

















