Vue3的`effect`函数是如何驱动组件重新渲染的?
在 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` 都会被放入一个待执行队列——注意,不是直接同步执行,而是通过微任务队列进行调度。这种异步批处理的设计,保证了你在同一个事件循环里多次修改数据,组件只会重新渲染一次。
我画了一个简化流程:
1. 修改响应式数据 → `trigger` 通知相关 effects
2. effect 被标记为 dirty,并推入调度器队列
3. 微任务执行时,调度器取出 effect,调用 runner
4. runner 再次执行渲染逻辑,生成新 vnode
5. 与旧 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 走一遍链路。
转载请注明出处,版权归原作者所有。
管理员
黑卡会员