likes
comments
collection
share

React Diff 有key和没key混合的时候怎么diff

作者站长头像
站长
· 阅读数 16

网上有太多的文章都在说React Diff 但是他们都是讲了基本的原理,但是真正使用的时候真的是这样么?

根据大佬们的源码解析,有以下几种场景

  1. 一次循环比对所有的节点,全部都能复用,这是非常理想的情况
  2. 第一循环完之后,老fiber/新fiber 其中有一个遍历完了,如果老fiber还剩下节点,那么标记删除,如果新fiber还剩下节点,标记新增
  3. 第一次循环中途停下来了,这之后使用了索引对比,就是把剩下的oldFiber节点通过[key]: fiber的方式存在一个map中,记录第一次遍历时候的最后一个索引为lastIndex, 然后开始遍历newFiber.sibling,拿遍历当前节点的key去map中找老节点有没有,如果没有就标记新增,进行下一个节点比对,如果有,判断老fiber中的索引,跟当前索引值哪个大,如果老fiber大,就按兵不动,进行下一步对比,如果老fiber节点索引比较小,那么节点复用的同时,把节点放在lastIndex后面,这样以此类推进行处理,当然这样处理完也有可能回到第二步,处理2个链表剩余的节点

这里有比较严重的一个问题,几乎没有人回答,就是这个map怎么生成的,因为不是所有的节点都有key值,有key和无key的节点穿插在里面,这个算法还真的是这样么?几乎所有的文章都默认了节点上是有key的,也有答主说会以老fiber的index作为key,我表示怀疑

下面就用案例一起看看吧

基础代码

import React, { useEffect, useState } from 'react';
function A() {
  useEffect(() => {
    console.log('[create]:a');
    
    return () => {
      console.log('[unmount]: a')
    }
  }, [])

  return 'a';
}

function B() {
  useEffect(() => {
    console.log('[create]:b')
    return () => {
      console.log('[unmount]: b')
    }
  }, [])
  return 'b'
}


export default function DiffComponent() {

  const [changeDiff, setChangeDiff] = useState(0);
  
  console.log('[changeDiff]:', changeDiff);

  return <>
    <div>{changeDiff ?'diff之前' : 'diff之后'}<button onClick={() => setChangeDiff(changeDiff+1)}>diff</button></div>
    <div>
      <A />
      <A />
      <A />
      <B />
      <B />
      <B />
    </div>
  </>
}

无key的时候(按照顺序比对)

常规情况


 <div>
  <A />
  <A />
  <A />
  <B />
  <B />
  <B />
</div>

React Diff 有key和没key混合的时候怎么diff

常规情况不管diff怎么点,组件都不会打印,说明复用了

长度相同,但是会有增减

  <div>
      {
        changeDiff == 0
        ? (
          <>
             <A />
             <A />
             <B />
          </>
        ): (
          <>
            <A />
            <B />
            <B />
          </>
        )
      }
    </div>

React Diff 有key和没key混合的时候怎么diff

长短不一的问题

  <div>
      {
        changeDiff == 0
        ? (
          <>
             <A />
             <A />
             <A />
          </>
        ): (
          <>
            <A />
            <A />
            <B />
            <B />
            <B />
          </>
        )
      }
    </div>

React Diff 有key和没key混合的时候怎么diff

A和B组件总量相同,前后顺序不同

 <div>
      {
        changeDiff == 0
        ? (
          <>
             <A />
             <A />
             <B />
             <B />
          </>
        ): (
          <>
            <B />
            <B />
            <A />
            <A />
          </>
        )
      }
    </div>

React Diff 有key和没key混合的时候怎么diff

有key的时候,符合绝大数文章的diff算法

总量相同,key相同,顺序不同

<div>
      {
        changeDiff == 0
        ? (
          <>
             <A key="1" />
             <A key="2"  />
             <B key="3"  />
             <B key="4"  />
          </>
        ): (
          <>
            <B key="4"  />
            <B key="3"  />
            <A key="2"  />
            <A key="1"  />
          </>
        )
      }
    </div>

React Diff 有key和没key混合的时候怎么diff

组件全部复用,基于上一个案例,有没有key逻辑是不一样的,所以多的案例就不写了,符合大家的预期

混合key进行Diff,建议先自己思考答案

  <div>
      {
        changeDiff == 0
        ? (
          <>
             <A  />
             <A key="2"  />
             <B key="3"  />
             <B key="4"  />
          </>
        ): (
          <>
            <B key="4"  />
            <B key="3"  />
            <A key="2"  />
            <A  />
          </>
        )
      }
    </div>

这个A到底会不会重新重建?

React Diff 有key和没key混合的时候怎么diff

有文章说老fiber会用索引为key来创建map真的是这样么?

 <div>
      {
        changeDiff == 0
        ? (
          <>
             <A  />
             <A key="2"  />
             <B key="3"  />
             <B key="4"  />
          </>
        ): (
          <>
            <B key="4"  />
            <B key="3"  />
            <A key={0} />
            <A key="2"  />
           
          </>
        )
      }
    </div>

事实证明还是重新创建了

React Diff 有key和没key混合的时候怎么diff

混合key的时候,什么情况下复用

 <div>
      {
        changeDiff == 0
        ? (
          <>
             <A key="2"  />
             <B key="3"  />
             <A  />
             <B key="4"  />
          </>
        ): (
          <>
            <B key="4"  />
            <B key="3"  />
            <A />
            <A key="2"  />
           
          </>
        )
      }
    </div>

React Diff 有key和没key混合的时候怎么diff

只有相同的索引下才会复用

所以,在diff的过程中应该加上,如果没key的话,会去老fiber里面找相同位置的fiber进行比对,判断是否需要复用,如果有key,那么去map里面捞,比对类型和索引

转载自:https://juejin.cn/post/7243087523756064805
评论
请登录