Vue.js 响应式系统触发组件渲染时机
Vue响应式系统通过自动化依赖追踪实现组件渲染:渲染函数执行时收集依赖,数据变更触发setter,将依赖推入异步更新队列,批量重渲染。父组件props变更仅当子组件实际使用时才触发更新。此外,$forceUpdate、triggerRef及keep-alive激活等场景也可间接驱动渲染。
先说结论:Vue的响应式系统,本质上是一套“自动化”的依赖追踪机制。它不像传统的轮询方案那样需要定时检查,也不依赖手动触发更新,而是通过数据读取和变更的“自然联动”,在恰当的时机静默地介入。整个过程的核心在于两个关键步骤:渲染函数执行时收集依赖,以及响应式数据被修改时通知重渲染。

首次挂载:渲染函数执行 → 自动收集依赖
组件首次渲染时,Vue会调用render函数来生成虚拟DOM。这个过程会读取所有用到的响应式数据,比如ref.value或reactive.obj.name。每次读取都会触发getter,此时,如果当前存在一个活跃的副作用(比如组件的render effect),这个effect就会被自动记录到对应响应式属性的依赖池中。
这里有几个细节值得注意:
- 嵌套访问,比如
state.user.profile.avatar,会逐层触发getter,实现深度依赖的记录。 - 模板中写的
{{ count }},底层等价于render函数里访问count.value,同样会完成依赖收集。
数据变更:setter被调用 → 触发更新队列
当响应式数据被修改时,比如count.value++或state.msg = 'new',setter或Proxy的set拦截器会被触发,紧接着启动更新流程:
- 根据变更的字段(比如
'msg'),找到对应的依赖池。 - 遍历依赖池中所有已登记的Watcher或effect,包括组件的渲染effect。
- 将它们推入异步更新队列,这个队列基于microtask机制,确保在同一事件循环内不会重复执行。
- 待队列清空后,批量重新执行相关的render函数,生成新的虚拟DOM,并进行diff更新。
父组件更新:props变更 → 子组件可能重渲染
子组件是否重渲染,完全取决于它是否实际使用了发生变化的props。如果模板或render函数中读取了某个prop,比如{{ title }},那么该prop的变更会触发子组件的依赖收集链路,进而重跑render。反之,如果props改变了,但子组件没有在渲染逻辑中读取它,就不会触发更新——Vue不会做无差别的刷新。
即使子组件的data没有变化,只要父组件传递的响应式prop被读取且发生了变更,子组件仍然会走完整的更新流程。这一点是Vue响应式系统的核心优势之一。
其他触发场景:非数据变更也能间接驱动
有些操作本身不修改数据,但会打破响应式链路或强制刷新:
this.$forceUpdate():跳过依赖追踪,直接标记组件为“需要更新”,适用于新增未劫持属性后的补救场景。triggerRef():针对shallowRef主动唤醒依赖,常用于内部状态需要手动通知的场景。- keep-alive组件激活时:虽然数据没有变化,但Vue会重新执行render,确保视图与缓存状态一致。
