activeeffect是全局变量,用于标记当前执行的effect实例,使track能准确收集依赖;其通过try-finally实现栈式切换,确保嵌套effect依赖隔离,配合cleanup清除自身旧依赖,避免误触发。

activeEffect 的切换本质是“执行栈式管理”,不是简单赋值替换,而是靠函数调用时的入栈与出栈来维持正确的依赖归属。
activeEffect 是什么角色
它是一个全局变量,只在某个 effect 函数正在执行期间被设为当前 effect 实例。它的唯一作用是:让 track 知道“此刻正在收集哪个副作用的依赖”。没有它,所有 get 操作都会往同一个 effect 上挂依赖,导致分支混乱、重复触发或漏触发。
嵌套 effect 时怎么保证不串包
关键在 ReactiveEffect.run() 方法里的 try-finally 结构:
- 进入子 effect 前,先保存上一级 activeEffect(比如 effect1)
- 把 activeEffect 设为当前子 effect(比如 effect2)
- 执行子 effect 内部逻辑,期间所有 track 都绑定到 effect2
- 子 effect 执行完,finally 中恢复上一级 activeEffect(effect1)
这就模拟了一个轻量级执行栈——effect1 → effect2 → effect1,依赖收集严格按嵌套层级隔离。
为什么 cleanup 要配合 activeEffect 使用
cleanup 不是独立操作,它发生在每次 effect 重新运行前,且只清理 当前 activeEffect 自己记下的那些依赖集合。每个 effect 实例都持有自己的 deps 数组(比如 effect1.deps = [nameDep, ageDep]),cleanup 就是遍历这个数组,从每个 dep Set 里删掉自己。
这样,当分支切换(如 if(obj.ok) 改变)导致某些属性不再被访问时,旧的依赖关系就被自动清除,下次 trigger 就不会误触发已失效的 effect。
track 如何判断该不该收集
track 开头就有一句守门逻辑:if (!activeEffect) return。这意味着:只有在 effect.run() 执行过程中发生的 get,才会建立依赖;组件渲染函数外随便读个响应式字段(比如 console.log(obj.name)),不会进 bucket,也不会污染依赖图。
这也解释了为什么 watchEffect 会立即执行一次——它内部调用了 effect,让 activeEffect 生效,从而启动整个 track → trigger 闭环。











