响应式系统通过在trigger阶段过滤当前活跃effect阻断obj.foo++的无限循环。其原理是读写同属一个effect上下文,若不筛选会导致自调用;Vue 3源码中用effectFn !== activeEffect跳过自身;scheduler支持异步批量更新;辅以深度限制和activeEffects去重确保稳定。

响应式系统防止 obj.foo++ 类操作引发无限死循环,核心在于**阻断“读-写-再读-再写”的副作用闭环**。这不是靠规避写法,而是靠在响应式触发链中插入守卫逻辑——尤其在 trigger 阶段主动过滤掉正在执行中的同一个 effect。
根本原因:读写发生在同一副作用上下文中
执行 obj.foo++ 实际等价于:const temp = obj.foo; // 触发 get → track 收集当前 activeEffectobj.foo = temp + 1; // 触发 set → trigger 通知所有依赖 effect
由于整个表达式在同一个 effect 函数内运行,track 记下的 activeEffect 和 trigger 即将执行的 effect 是同一个函数。若不加判断,它会反复调用自身,直到栈溢出。
源码级守卫:trigger 中跳过当前活跃 effect
Vue 3 的 trigger 实现中加入了关键判断:
- 从依赖映射
depsMap中取出对应 key 的所有 effect 集合 - 遍历该集合时,用
effectFn !== activeEffect做筛选 - 只把“非当前正在执行”的 effect 加入待执行队列
这段逻辑直接切断了自调用路径,让 obj.foo++ 最多触发一次更新,而非无限递归。
调度器(scheduler)提供更精细的控制出口
仅靠守卫能防崩溃,但无法解决“值变了却没及时反映”的问题。因此响应式系统支持传入 scheduler:
- 当
trigger检测到需要更新时,不立即执行 effect,而是调用用户定义的 scheduler - 典型实现是把更新任务推入微任务队列(如
queueMicrotask),实现批量、异步、去重更新 - 这既避免同步死循环,又保障视图更新不被重复触发
额外防护:深度限制与执行栈追踪
部分响应式库还会叠加两层保护:
-
递归深度计数:在 effect 执行前递增
effectDepth,超过阈值(如 100 层)强制中断,防止意外嵌套 -
effect 去重 Set:维护一个全局
activeEffects集合,在 effect 进入时加入、退出时移除,避免同一 effect 被重复加入调度队列
这些机制共同构成防御纵深,让响应式系统在面对看似危险的操作时依然稳定可靠。

















