2025.09.09
倍增触发:多依赖场景下的通知风暴解析
❓ 问题引入:几何级增长的触发次数
解决了单个响应式变量的重复收集问题后,我们将目光转向更复杂的 多依赖 场景。当一个 effect 同时依赖多个 ref
时,触发次数再次失控。
<button id="btn">增加计数</button>
<script type="module">
// ... 导入 ref, effect
const flag = ref(true)
const count = ref(0)
const e = effect(() => {
flag.value // 依赖 1
count.value // 依赖 2
console.count('effect 触发次数')
})
btn.onclick = () => {
count.value++ // 仅修改 count
}
</script>
现象: 每次点击按钮,effect 的触发次数呈几何级增长。

🔬 详细分析:Link 节点复用失效
- 初始化阶段: 建立有序双向链表
首次执行 effect时,依赖收集是清晰有序的:- 读取顺序:
flag.value->count.value link顺序:flag依赖link1,count依赖link2Effect内部:deps链表link1->link2
- 读取顺序:
- 第一次点击: 更新
count.value当count.value++触发更新,effect重新执行。
2.1 🔴 重新读取flag.value
- 状态标记:
effect.run()将depsTail重置为undefined, 进入复用模式 - 当前
link:effect.deps执行link1 - 复用检查: 检查
link1.dep是否为当前依赖flag - 结果:
link1.dep(flag) === dep(flag)✅ 复用成功 - 指针移动:
depsTail移动到link1
2.2 🔵 重新读取count.value
- 当前
Link: 由于复用成功,depsTail现在指向link1 - 复用检查: 再次检查
(sub.deps && !sub.depsTail)条件 -> 不成立 (depsTail不为undefined) - 系统判断: 判断为 "正在尾部追加依赖" 状态
- 结果: ❌ 冗余创建,系统忽略了
link2的存在,创建了新的link3节点,并将其追加到link1之后。]
- 状态标记:
- 问题后果: 链表分叉与几何级增长
由于
count的依赖没有复用link2,反而创建了冗余的link3,导致:- 依赖链表结构错误:
effect的deps链变化成了link1->link3, 且link2被遗弃但并未清理 - 下一次触发: 随着
link3的存在, 在下次更新中再次触发effect执行, 产生单依赖场景相似的依赖重复收集 的恶性循环
- 依赖链表结构错误:
💡 核心问题
复用机制的判断逻辑过于简单,它只适用于 依赖顺序与上次收集顺序完全一致 且 数量不变
的场景。一旦读取顺序或依赖数量发生变化,depsTail 的状态就会被错误设置,导致节点冗余创建。