Vue响应式系统通过“读取即订阅”实现依赖收集:组件首次渲染时访问响应式数据,自动将渲染Watcher加入其Dep或effect集合;computed和watch也各自创建独立Watcher;新增属性、数组索引赋值、遍历非响应式对象、异步读取等场景会漏收集。

Vue 响应式系统在组件内完成依赖收集,本质是“读取即订阅”——组件首次渲染时,只要访问了响应式数据(比如 this.msg 或 props.count),Vue 就会把当前组件的渲染任务记到那个数据的“关注名单”里。
每个组件自带一个渲染 Watcher
组件实例创建后,Vue 会自动为它创建一个专属的渲染 Watcher。这个 Watcher 不是静态配置出来的,而是在组件第一次执行 render() 函数时被激活并设为“当前活跃”(即 Dep.target 或 Vue 3 中的 activeEffect)。
- 它会在 render 过程中逐行执行模板逻辑,例如读取
this.name、this.list.length、computed.fullName等; - 每次读取,都会触发对应属性的 getter(Vue 2)或 get 拦截器(Vue 3),进而把该 Watcher 写入属性背后的 Dep(Vue 2)或 effect 集合(Vue 3);
- 一个组件可能同时依赖多个属性,每个属性都独立维护自己的依赖列表,互不干扰。
依赖只在真正读取时发生
依赖收集不是靠扫描代码或提前声明,而是严格发生在运行时的数据访问瞬间。没被 render 函数、计算属性或 watch 回调实际读到的属性,就不会被任何 Watcher 订阅。
使用 `@ainative/vue-sdk` 为 Vue 3 应用添加 AI 聊天功能。适用于以下场景:(1) 安装 `@ainative/vue-sdk`,(2) 在 Vue 组件中使用 `useChat` 组合式函数,(3) 提供……
- 例如模板里写了
{{ user?.profile?.age }},但user是 null,那profile和age的 getter 根本不会执行,也就不会收集依赖; - 再如
v-if="showList" > {{ items[0] }} </div>,当showList为 false 时,items[0]不会被读取,自然不收集; - 同理,计算属性内部没用到的 data 字段,也不会出现在它的依赖列表中。
计算属性和侦听器也各自拥有 Watcher
组件内的 computed 和 watch 并非复用渲染 Watcher,而是新建独立 Watcher,各自建立依赖关系。
立即学习“前端免费学习笔记(深入)”;
-
computed: { total() { return this.list.length * this.price } }—— 它的 Watcher 在首次求值时,分别触发list.length和price的 getter,把自身写进这两个属性的 Dep/effect 集合; -
watch: { userId(val) { fetchUser(val) } }—— Vue 内部会创建一个 Watcher 去读取this.userId,从而完成对userId的依赖登记; - 这些 Watcher 彼此隔离:修改
userId不会触发渲染 Watcher 更新,除非模板里也用了它。
常见“漏收集”的典型场景
有些操作看似访问了响应式数据,实则绕过了 getter,导致依赖未建立,后续变化无法触发更新。
-
对象新增属性:
this.obj.newKey = 'val'(Vue 2)或未用reactive({})包裹的裸对象赋值,因未触发 defineProperty/set 或 Proxy.get,不收集; -
数组索引直接赋值:
this.arr[1] = 'x'(Vue 2),Object.defineProperty 无法拦截数字索引,getter 不触发; -
遍历未响应式字段:
Object.keys(this.normalObj)中的normalObj若未经过reactive或data()初始化,则无 getter,不收集; -
异步读取延迟:在
setTimeout或 API 回调里才访问this.flag,此时渲染已结束,activeEffect已清空,不会收集。

















