2025.09.10
迭代复用:高效解决多依赖重复收集
前文我们发现,在多依赖场景下,即使引入了节点复用机制,由于 依赖读取顺序的变化 或 状态管理不当,依然会导致冗余 Link
节点的创建。问题的核心在于:复用检查缺乏迭代能力。
💣 多依赖场景下的陷阱:Effect 为何被成倍触发?
前文我们发现,在多依赖场景下,即使引入了节点复用机制,由于 依赖读取顺序的变化 或 状态管理不当,依然会导致冗余 Link
节点的创建。问题的核心在于:复用检查缺乏迭代能力。
原始实现的局限性
我们原始的复用逻辑,通过将 depsTail 临时置为 undefined 来标记“重新执行中”状态,但它存在以下致命缺陷:
| 缺陷 | 描述 | 后果 |
|---|---|---|
| 局限检查 | 复用逻辑仅检查并对比依赖链表的 第一个节点 (sub.deps)。 | 无法处理第二个及后续依赖的复用。 |
| 状态变更过早 | 一旦第一个节点复用成功,depsTail 会立即指向该节点。 | 后续的依赖项无法进入复用检查状态(因为 depsTail 不再是 undefined)。 |
💡 优化思路
要解决这个问题,我们需要引入一个 迭代机制,在复用阶段让检查指针(由 depsTail 充当)能够沿着上次收集的链表逐步前进,而不是每次都回到起点或被错误地锁定。

注:之前的图解中有误,感谢指正 😊(effect 的尾节点应为:subsTail → depsTail)
depsTail 状态 | 含义 | 下一个待检查节点(nextDep) |
|---|---|---|
undefined | 链表尚未开始复用(或链表为空)。 | sub.deps (头节点) |
| 存在 | 当前已经复用到此节点 | sub.depsTail.nextDep (下一个节点) |
🛠️ 代码优化
// system.ts -> link 函数
export function link(dep: Dep, sub: ReactiveEffect): void {
const currentDep = sub.depsTail
// 1. 确定下一个待检查的节点 (迭代前进的关键)
const nextDep = (currentDep === undefined)
? sub.deps // 情况一:从链表头部开始检查
: currentDep.nextDep // 情况二:从已复用节点的下一个节点开始检查
// 2. 执行复用检查
if (nextDep && nextDep.dep === dep) {
// 3. 复用成功:移动 depsTail 指针,指向当前复用的节点
sub.depsTail = nextDep
}
// 4. 如果检查失败,则执行创建新节点的逻辑
// ... (创建新 Link 节点,并将其追加到当前的 depsTail 之后)
}
🚀 完整流程
| 阶段 | 动作 | currentDep | nextDep | 结果 |
|---|---|---|---|---|
| 重置标记 | effect.run() 执行 | depsTail -> undefined | ||
读取 flag | link(flag -> effect) | undefined | sub.deps(link1) | link1.dep === flag => 复用成功depsTail -> link1 |
读取 count | link(count -> effect) | link1 | link1.nextDep(link2) | link2.dep === count => 复用成功depsTail -> link2 |
| 收集完成 | 链表结构保持不变, 没有冗余节点 |