Skip to content

在之前的做法是

  1. run 函数首先会将 depsTail 置空
    ts
    // effect.ts
    run() {
      const prevSub = activeSub
      activeSub = this
    
      this.depsTail = undefined
      // ...
    }
  2. 在依赖收集的时候, depsTail 会被指向已复用的节点
    ts
      export 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` 节点是否是我们要复用的节点
    ts
     const 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
}

完整流程

  1. 获取 depsTail 当前值(currentDep = sub.depsTail)
  2. 根据 depsTail 决定检查的节点
    • 如果 depsTailundefined, 说明是从头节点开始检查(nextDep = sub.deps)
    • 如果 depsTail 存在, 说明是从中途开始检查(nextDep = currentDep.nextDep)
  3. 检查 nextDep 是否存在且其 dep 是否等于当前 dep
    • 如果是, 复用成功, 将 depsTail 指向 nextDep
    • 如果不是, 继续创建新节点

最后更新时间: