Appearance
在之前的做法是
run
函数首先会将depsTail
置空ts// effect.ts run() { const prevSub = activeSub activeSub = this this.depsTail = undefined // ... }
- 在依赖收集的时候,
depsTail
会被指向已复用的节点tsexport function link(dep,sub){ if(sub.depsTail === undefined && sub.deps){ if(sub.deps.dep === dep){ // 复用成功 sub.depsTail = sub.deps return } } // 如果不符合重新创建新节点 }
问题出现的原因是:
- 检查范围过小: 复用逻辑只检查并对比依赖链表的第一个节点(
sub.deps
) - 状态提前改变: 一旦第一个节点被复用,
depsTail
就不再是undefined
, 导致后续节点无法复用
解决思路
在原有的 link
函数中, 增加额外的检查逻辑
当第一个 if 条件不满足时, 但 depsTail
确实存在的时候, 不应该从头节点开始检查, 而是应该从 depsTail.nextDep
开始检查
之前的图中有错误, 感谢好哥哥提醒 😊(effect 的尾节点: subsTail -> depsTail)
检查逻辑是
- 如果尾节点
depsTail
存在, 并且这儿尾节点还有下一个
nextDep节点, 应该检查这个
nextDep` 节点是否是我们要复用的节点tsconst currentDep = sub.depsTail // 依赖项表头节点的 ref 与之前连接的 ref 相等的话表示之前收集过依赖 if(currentDep === undefined && sub.deps) { if(sub.deps.dep === dep) { // 移动尾节点,指向复用的节点 sub.depsTail = sub.deps return } }else if(currentDep) { // 尾节点的 nextDep 所连接的 ref 等于当前要连接的 ref, 说明之前收集过依赖 if(currentDep.nextDep?.dep === dep) { // 移动尾节点,指向复用的节点 sub.depsTail = currentDep.nextDep return } }
代码简化
无论是从头开始还是从中途开始, 目标都是 找到下一个待检查的节点
ts
const currentDep = sub.depsTail
// 根据 currentDep 是否存在, 来决定下一个要检查的节点
const nextDep = currentDep === undefined ? sub.deps : currentDep.nextDep
if (nextDep && nextDep.dep === dep) {
sub.depsTail = nextDep
return
}
完整流程
- 获取
depsTail
当前值(currentDep = sub.depsTail)
- 根据
depsTail
决定检查的节点- 如果
depsTail
是undefined
, 说明是从头节点开始检查(nextDep = sub.deps
) - 如果
depsTail
存在, 说明是从中途开始检查(nextDep = currentDep.nextDep
)
- 如果
- 检查
nextDep
是否存在且其dep
是否等于当前dep
- 如果是, 复用成功, 将
depsTail
指向nextDep
- 如果不是, 继续创建新节点
- 如果是, 复用成功, 将