做diff_重新梳理 diff 思路之残破链表

90a836f90bf61dac5d02e4d1b1f5f952.png

这可能是一篇很难理解的文章,我想了许久,但我不想废话

我们经常听说 react 的 diff 算法是 O(n),说的其实是最好情况

diff (oldVnode, newVnode){
  if (oldVnode.type !== newVnode.type){
    // 命中这里,则替换节点,放弃子树遍历,全部命中这里,则为 O(n)
  }else{
    // 命中这里,则遍历字节点,则为 O(n^2)
  }
}

但是链表不一样,它除了做 diff,还要做链表生成的逻辑,children 是一定要遍历的

  while (true) {

      // 生成链表


      for (var i = 0, prev; i < children.length; i++) {
        const fiber = children[i]
        fiber.parent = wip
        if (i > 0) {
          prev.sibling = fiber
        } else {
          parent.child = fiber
        }
        prev = fiber
      }


      // 遍历链表


      if (fiber === root) {
        break
      }
      while (!fiber.sibling) {
        if (!fiber.return || fiber.return === root) {
          return;
        }
        fiber = fiber.return
      }
      fiber = fiber.sibling
    }

上面是整个链表的遍历,必然的 O(n^2)

我们要想实现 type 的优化,唯一的办法只有跳出主循环,而跳出主循环的决定性因素在于链表生成的部分

for (var i = 0, prev, oldFiber = wip.alternate; i < children.length; i++) {
        const fiber = children[i]
        fiber.parent = parent
        if (oldFiber) {
            fiber.alternate = oldFiber
            oldFiber = oldFiber.sibling
        }

        if (i > 0) {
          prev.sibling = fiber
        } else {
          parent.child = fiber
        }
        prev = fiber
      }

如上,我们使用双缓冲制造了一个 oldFiber

for (var i = 0, prev, oldFiber = wip.alternate; i < children.length; i++) {
      const fiber = children[i]
      if (oldFiber) {
          if(oldFiber.type !== fiber.type){
              continue // 放弃链表生成
          }else{
            oldFiber = oldFiber.sibling
            fiber.alternate = oldFiber
          }
      }

      fiber.parent = wip

      if (i > 0) {
          prev.sibling = fiber
      } else {
          parent.child = fiber
      }
      prev = fiber
 }

就这样,我们对这个循环,增加了根据 type 放弃子树渲染的优化可能

然后我们就可以在这个链表上尝试增加常规算法了

但是这还没完,实际想要真正使用递归算法,仍旧非常困难,但是无论如何

链表也是可以做到对等的 diff 算法的

react 应该是没有这个优化的

fre1 没有这种根据 type 打断子树的优化,是因为抄 react 的时候没有发现……

这个优化实际上只是打断子树,dom 操作还是不会少的

if(oldFiber.type !== fiber.type){
     const op = () => createElment(fiber) // 这里需要递归操作 dom
     commit.push(op)
     continue // 放弃链表生成
}

react 之所以没有这个优化,我猜是因为它不想递归操作 dom

因为实际上 react 的 commitWork 遍历的仍旧是链表,所以它必须保证子链的生成是完整的,因为还有 ref,effects 啥的甚至一堆我们不知道的东西要处理

fre 没有这么多东西,链表的作用只是用来实现时间切片的而已

而且 dom 操作也是同步的……根本不需要使用链表

总结

总的来说就是,生成一颗完整的链表,比如需要遍历每一个节点的子树,那么复杂度必然是 O(n^2)

我么要想搞 O(n) 的情况,必然要放弃子树,放弃它就不能生成它,所以我们只生成一颗残破的树

我把它成为【残破链表】,以后大家用到类似的优化,一定要记着我!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值