• FiberRoot和RootFiber


    ReactDOM.render源码解析
    上文提到fiberRootrootFiber, 它们到底是什么, 有什么关联?

    两者的关系:

    在这里插入图片描述

    为什么要区分?

    在应用中ReactDOM.render可以被多次调用, 它们可以拥有不同的rootFiber(FiberNode), 但是整个应用的根节点只能是一个, 那就是fiberRoot(fiberRootNode)

    从上图可以看出fiberRoot(fiberRootNode)的current指向的是rootFiber(FiberNode), 也就是当前页面中所需要渲染的内容对应的Fiber树, 即 current fiber 树

    FiberRoot(fiberRootNode)

    它是整个应用的根节点, 绑定在container._reactRootContainer, 也就是绑定在真实DOM节点的_reactRootContainer属性上, 就算是重复调用ReactDOM.render, FiberRoot(fiberRootNode)还是不变的, 内部做了判断. 详情可以看源码(源码地址)

    createFiberRoot 源码

    源码地址

    export function createFiberRoot(
      containerInfo: any,
      tag: RootTag,
      hydrate: boolean,
      hydrationCallbacks: null | SuspenseHydrationCallbacks,
    ): FiberRoot {
      // 创建fiberRoot(fiberRootNode)
      const root: FiberRoot = (new FiberRootNode(containerInfo, tag, hydrate): any);
      if (enableSuspenseCallback) {
        root.hydrationCallbacks = hydrationCallbacks;
      }
    
      // Cyclic construction. This cheats the type system right now because
      // stateNode is any.
      const uninitializedFiber = createHostRootFiber(tag);// 创建rootFiber(fiberNode)
    
      // 把rootFiber挂载到fiberRoot的current属性上  
      root.current = uninitializedFiber;
    
      // 把fiberRoot挂载到rootFiber的stateNode属性上
      uninitializedFiber.stateNode = root;
    
      // 初始化更新队列
      initializeUpdateQueue(uninitializedFiber);
    
      return root;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    这一段代码, 可以很好的解释上面fiberRoot和rootFiber关系图

    FiberRootNode 对象

    源码地址

    type BaseFiberRootProperties = {|
      // The type of root (legacy, batched, concurrent, etc.)
    
      /* 
      export type RootTag = 0 | 1 | 2;
      export const LegacyRoot = 0;
      export const BlockingRoot = 1;
      export const ConcurrentRoot = 2;
      */
      tag: RootTag,
    
      // root节点, 也就是ReactDOM.render(, document.getElementById('root'))的第二个参数
      containerInfo: any,
      
      pendingChildren: any,
      
      //current:Fiber对象 对应的是 root 节点,即整个应用根对象
      current: Fiber,
    
      pingCache: WeakMap<Wakeable, Set<mixed>> | Map<Wakeable, Set<mixed>> | null,
    
      // A finished work-in-progress HostRoot that's ready to be committed.
      finishedWork: Fiber | null,
      // Timeout handle returned by setTimeout. Used to cancel a pending timeout, if
      // it's superseded by a new one.
      timeoutHandle: TimeoutHandle | NoTimeout,
      // Top context object, used by renderSubtreeIntoContainer
      context: Object | null,
      pendingContext: Object | null,
      // Determines if we should attempt to hydrate on the initial mount
      +hydrate: boolean,
    
      // Used by useMutableSource hook to avoid tearing during hydration.
      mutableSourceEagerHydrationData?: Array<
        MutableSource<any> | MutableSourceVersion,
      > | null,
    
      // Node returned by Scheduler.scheduleCallback. Represents the next rendering
      // task that the root will work on.
      callbackNode: *,
      callbackPriority: LanePriority,
      eventTimes: LaneMap<number>,
      expirationTimes: LaneMap<number>,
    
      pendingLanes: Lanes,
      suspendedLanes: Lanes,
      pingedLanes: Lanes,
      expiredLanes: Lanes,
      mutableReadLanes: Lanes,
    
      finishedLanes: Lanes,
    
      entangledLanes: Lanes,
      entanglements: LaneMap<Lanes>,
    |};
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56

    在这里插入图片描述

    rootFiber(fiberNode)

    rootFiber所在组件树的根节点,rootFiber在每次重新渲染的时候会重新构建。
    本身就是一个普通的fiberNode, 上面通过createHostRootFiber(tag)创建, 其实最终还是通过createFiber函数创建的.

    export function createHostRootFiber(tag: RootTag): Fiber {
      ....
      return createFiber(HostRoot, null, null, mode);
    }
    const createFiber = function(
      tag: WorkTag,
      pendingProps: mixed,
      key: null | string,
      mode: TypeOfMode,
    ): Fiber {
      // $FlowFixMe: the shapes are exact here but Flow doesn't like constructors
      return new FiberNode(tag, pendingProps, key, mode);
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    FiberNode

    源码地址

    function FiberNode(
      tag: WorkTag,
      pendingProps: mixed,
      key: null | string,
      mode: TypeOfMode,
    ) {
      // Instance
    
    	/**
    	export const FunctionComponent = 0;
    	export const ClassComponent = 1;
    	export const IndeterminateComponent = 2; 
    	export const HostRoot = 3; 
    	export const HostPortal = 4; 
    	export const HostComponent = 5;
    	export const HostText = 6;
    	export const Fragment = 7;
    	*/
       // tag 组件的类型 是函数组件, 类组件, Fragment, 原生的标签...
      this.tag = tag;
      
      this.key = key; 
    
      this.elementType = null;
    
      // 对于 函数组件,指函数本身,对于类组件,指class
      this.type = null;
    
      // Fiber对应的真实DOM节点
      this.stateNode = null; 
    
      // 以下属性用于连接其他Fiber节点形成Fiber树。
    
      // 指向父fiber节点
      this.return = null;
      
      // 指向第一个子fiber节点
      this.child = null;
    
      // 指向第一个兄弟fiber节点
      this.sibling = null;
      this.index = 0;
    
      // 真实的DOM属性
      this.ref = null;
    
      // 新传入的 props
      this.pendingProps = pendingProps;
    
      // 之前的 props
      this.memoizedProps = null;
    
      // 更新队列,用于暂存 setState 的值
      this.updateQueue = null;
    
      // 之前的 state
      this.memoizedState = null;
      this.dependencies = null;
    
      this.mode = mode;
    
      // 保存本次更新会造成的DOM操作。比如删除,移动
      this.flags = NoFlags;
      this.nextEffect = null;
    
      this.firstEffect = null;
      this.lastEffect = null;
    
      this.lanes = NoLanes;
      this.childLanes = NoLanes;
    
    
      //用于链接新树和旧树;旧->新,新->旧
      this.alternate = null;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75

    stateNode和alternate属性

    stateNode: Fiber对应的真实DOM节点

    查看stateNode, 测试代码:

    function App(props) {
      const content = <div className="app-div"></div>;
      console.log(content);
      return content;
    }
    ReactDOM.render(<App />, document.getElementById("root"));
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    在这里插入图片描述

    alternate属性
    第一个作用: 判断是首次渲染还是更新
    第二个作用: 是当前更新的fiber tree(workInProgress tree) 和 current fiber tree(old tree)的桥梁

    • 该属性在节点初始化的时候默认为空(this.alternate = null)。这个节点的作用就是用来缓存之前的 Fiber 节点,更新的时候会判断 fiber.alternate 是否为空来确定当前是首次渲染还是更新.
    • react更新的时候其实存在两颗fiber树 [将产生update的React Element和current Fiber Tree(也就是old Tree)进行比较, 最终在内存中生成workInProgress tree], 我们使用alternate去连接old tree.

    在这里插入图片描述

    查看alternate, 测试代码:

    function App(props) {
      const [a, setA] = React.useState("");
      const content = (
        <div
          className="app-div"
          onClick={() => {
            setA(Math.random());
          }}
        >
          haha{a}
        </div>
      );
      console.log(content); // 每次更新操作, 就打印出最新的fiber tree
      return content;
    }
    ReactDOM.render(<App />, document.getElementById("root"));
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    在这里插入图片描述

  • 相关阅读:
    百日刷题计划 ———— DAY1
    NX二次开发-通过点击按钮来控制显示工具条
    处于损害控制模式的微软表示,它将优先考虑安全性,而不是人工智能
    MATLAB Coder从入门到放弃
    Java笔记:GC日志
    EC200U 进行 QuecPython 固件烧录
    2021长安杯
    Nvidia Jetson/Orin +FPGA+AI大算力边缘计算盒子:加油站安全智能检测系统
    Python之进程,线程锁,Queue相关
    解决sentinel结合nacos实现集群限流(嵌入式)
  • 原文地址:https://blog.csdn.net/qq_39583550/article/details/125893250