CClara轻量论坛系统

Vue3的`effect`函数是如何驱动组件重新渲染的?

最长的电影
最长的电影 正式会员正式会员
发布于 2026-07-29 02:13 ·110 浏览 ·0 回复
内容摘要

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 都会被放入一个待执行队列——注意,不是直接同步执行,而是通过微任务队列进行调度。这种异步批处理的设计,保证了你在同一个事件循环里多次修改数据,组件只会重新渲染一次。

我画了一个简化流程:

  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 走一遍链路。

本文转载自 Clara轻量论坛系统,原文地址:https://www.leleweb.cn/thread-175.html
转载请注明出处,版权归原作者所有。
—/ 5暂无评分
还没有评分,来打第一分吧

全部回复 0

还没有回复,来抢沙发~