Vue3的`effect`函数是如何驱动组件重新渲染的?
Vue3 将组件渲染函数封装为 ReactiveEffect,首次渲染读取响应式数据并通过 track 建立依赖,数据变化经 trigger 后由 scheduler 放入微任务队列异步批处理,再执行 runner 重新渲染生成 vnode 并 diff 更新 DOM;
在 Vue3 的源码里,effect 是一个看似不起眼却贯穿整个响应式系统的核心函数。如果你曾经好奇过:当我修改一个 ref 或者 reactive 对象时,为什么页面上的组件会自动更新?答案就藏在 effect 与组件渲染函数的绑定关系中。今天我们不啃晦涩的源码,而是从“现象”出发,一步步拆解这个驱动机制。
从渲染函数说起
每个 Vue 组件在挂载时,都会执行一次 render 函数来生成虚拟 DOM。但 render 函数并不是被直接调用的,它被包裹在一个 effect 里。也就是说,组件实例的 update 方法本质上就是一个 effect 的 runner。
const componentEffect = () => {
// 执行 render,生成 vnode
const vnode = component.render()
// 后续 patch 逻辑...
}
当这个 effect 第一次执行时,它会读取模板中用到的所有响应式数据(比如 count.value)。而响应式数据的 track 机制会把这个 effect 记录为当前依赖。于是,组件和这些数据之间建立了一条“订阅关系”。
数据变化时的通知链
当你执行 count.value++ 时,Vue 内部会触发 trigger。此时,所有订阅了 count 的 effect 都会被放入一个待执行队列——注意,不是直接同步执行,而是通过微任务队列进行调度。这种异步批处理的设计,保证了你在同一个事件循环里多次修改数据,组件只会重新渲染一次。
我画了一个简化流程:
- 修改响应式数据 →
trigger通知相关 effects - effect 被标记为 dirty,并推入调度器队列
- 微任务执行时,调度器取出 effect,调用 runner
- runner 再次执行渲染逻辑,生成新 vnode
- 与旧 vnode 进行 diff,并更新真实 DOM
为什么是 effect 而不是 watch?
很多初学者会把 effect 和 watchEffect 混淆。实际上,watchEffect 就是基于 effect 实现的更高层 API。组件渲染用的就是底层的 effect 实例(ReactiveEffect),只不过它在创建时传入了一个 scheduler 调度函数,而不是简单的 fn。
这个 scheduler 的关键作用是:避免每次数据变化都同步执行更新,而是把更新任务交给调度器。这使得 Vue 可以支持 nextTick、flush: 'post' 这类特性,也让用户在使用组件时获得更平滑的渲染体验。
来看一个简化版的伪代码:
class ReactiveEffect {
constructor(fn, scheduler) {
this.fn = fn
this.scheduler = scheduler
}
run() {
// 执行时收集依赖
activeEffect = this
return this.fn()
}
trigger() {
if (this.scheduler) {
this.scheduler(this)
} else {
this.run()
}
}
}
组件更新时,Vue 传入的 scheduler 会把 job 加入队列,并在 nextTick 的 promise 解析后统一执行。
effect 的“嵌套”与渲染时机
还有一个有趣的现象:组件树本身是嵌套的,所以 effect 的执行也会有嵌套。Vue 3 维护了一个 effectStack,用来在嵌套执行时正确还原 activeEffect。否则,当父组件的 effect 读取子组件的响应式数据时,依赖关系会错乱。
这个设计细节直接保证了:即使在父组件渲染函数中访问了子组件的 setup 返回的 ref,依赖也能被正确追踪,不会出现“改子组件数据却更新父组件”的诡异情况。
总结
effect 是 Vue 3 响应式系统的心脏。它将组件的渲染函数转变为可追踪、可调度、可取消的“副作用任务”。数据只是触发信号,真正的更新动作由 effect 统一管理。理解了 effect,你就理解了 Vue 3 的更新机制,再去看 computed、watch、乃至 pinia 的底层实现都会豁然开朗。
想深入的话,不妨打开 @vue/reactivity 源码,搜索 ReactiveEffect 这个类,用断点慢慢跟踪一次数据更新。相信你会有种“原来是这么回事”的爽快感。毕竟,看源码不如跑源码,跑源码不如跟着 effect 走一遍链路。
转载请注明出处,版权归原作者所有。
正式会员






