Vue依赖收集与精准重渲染机制:首次渲染时通过getter拦截建立响应式依赖关系,数据变更后仅通知相关组件异步重渲染,结合虚拟DOM diff实现最小化更新。

Vue.js 渲染流程中,依赖收集与组件重渲染触发是一对紧密配合的机制:组件首次渲染时读取响应式数据,自动建立“谁依赖谁”的关系;数据变更后,仅通知真正关心它的组件重新执行 render,而不是全量刷新。
依赖收集发生在组件首次渲染时
当组件挂载(mount)并执行 render 函数 时,会访问 data、computed、props 等响应式字段。此时 Vue 的 getter 拦截器被触发:
- 若当前存在活跃的 effect(如组件的 render effect),则将该 effect 记录到对应响应式属性的 Dep(依赖集合)中;
- 每个 reactive 对象的每个属性(或 ref 的整个值)都维护独立的 Dep,用 Set 存储副作用函数;
- 嵌套对象或数组访问会逐层触发 get,实现深度依赖收集;例如访问
user.profile.name会分别收集 user、profile、name 三级依赖; - 条件分支中未执行的代码(如
v-if="false"内部的属性读取)不会触发收集,因为其 getter 根本没运行。
数据变更触发精准重渲染
当响应式数据被修改(如 count++ 或 arr.push()),setter 或代理 trap 执行 trigger 流程:
将一个或多个视频上传至YouTube。当用户想“上传到YouTube”“发YouTube”“批量上传”“upload to YouTube”“post videos to YouTube”或发布到YouTube时使用此技能。
- 根据变更的 key(如
'count')查到对应 Dep; - 把 Dep 中所有 effect(包括 render effect、watchEffect、computed effect)加入异步调度队列;
- Vue 使用微任务(
Promise.then或queueMicrotask)批量执行,同一 tick 内对同一个 effect 最多执行一次; - 组件 render effect 重新执行,生成新 VNode,再与旧 VNode diff,只更新真实 DOM 中变化的部分。
依赖收集不是无条件发生的
只有在“激活的副作用上下文”中读取响应式数据,才会被收集。常见例外情况:
立即学习“前端免费学习笔记(深入)”;
- 全局作用域直接打印
console.log(obj.count)—— 没有 active effect,不收集; - 普通函数内部读取(未被 effect 包裹)—— 不属于响应式执行上下文;
- 异步回调里首次访问响应式数据(如
setTimeout(() => console.log(state.x), 0))—— 此时 effect 已退出,也不收集; - 使用
toRaw()或markRaw()标记的对象,绕过 proxy 拦截,完全不参与依赖系统。
组件重渲染不等于 DOM 重绘
render effect 重新执行只是生成新的虚拟节点,真正影响性能的是后续的 diff 和 patch:
- 父子组件间按创建顺序更新(父先于子),确保 DOM 结构一致性;
- 用户定义的
watchEffect和计算属性会在 render 前执行,可能提前改变状态; - 使用
key或keep-alive可避免不必要的实例销毁/重建,减少重复初始化开销; - 频繁触发的简单响应式更新(如鼠标坐标)建议用
shallowRef或脱离响应式系统,避免过度收集。

















