【React源码】5.Fiber

139 阅读3分钟

Fiber可以解决哪些事

  • 工作单元、任务分解: Fiber最重要的功能就是作为工作单元,保存原生节点或组件节点对应信息(包括优先级),这些节点通过指针的形式形成Fiber树
  • 增量更新:通过jsx对象和current Fiber的对比,生成最小的差异补丁(也就是新增的部分),应用到真实节点上
  • 根据优先级暂停、继续、排列优先级:Fiber节点上保存了优先级,能通过不同节点优先级的对比,达到任务的暂停、继续、排列优先级等能力,也为上层实现批量更新、Suspense提供了基础
  • 保存状态: 因为Fiber能保存状态更新的信息,所以就能实现函数组件的状态更新,也就是hooks

Fiber的数据结构

FiberNode函数:因为版本不同,和下面的有点出入

Fiber的自带属性如下:

//ReactFiber.old.js
function FiberNode(
  tag: WorkTag,
  pendingProps: mixed,
  key: null | string,
  mode: TypeOfMode,
) {
  //作为静态的数据结构 保存节点的信息 
  this.tag = tag;// 对应组件的类型
  this.key = key;//key属性
  this.elementType = null;// 元素类型。react的elementType,就是 REACT_ELEMENT_TYPE
  this.type = null;//class、func、div字符串等
  this.stateNode = null;// 指向真实的dom节点,或者根节点指向FiberRootNode

  //作为fiber数架构 连接成fiber树
  this.return = null;//指向父节点
  this.child = null;//指向child
  this.sibling = null;//指向兄弟节点
  this.index = 0;

  this.ref = null;

  //用作为工作单元 来计算state
  this.pendingProps = pendingProps;
  this.memoizedProps = null;
  this.updateQueue = null;// 存放未计算的update
  this.memoizedState = null;// 当前的状态
  this.dependencies = null;

  this.mode = mode;
    
  //effect相关
  this.effectTag = NoEffect;
  this.nextEffect = null;
  this.firstEffect = null;
  this.lastEffect = null;

  //优先级相关的属性
  this.lanes = NoLanes;
  this.childLanes = NoLanes; // 所有子节点的优先级

  //current和workInProgress的指针
  this.alternate = null;
}

Fiber深度优先遍历

深度优先遍历

281647690755_.pic.jpg

Fiber双缓存

Fiber可以保存真实的dom,真实dom对应在内存中的Fiber节点会形成Fiber树,这颗Fiber树在React中叫current Fiber。也就是当前dom对应的Fiber树,而正在构建的Fiber树叫workInProgress Fiber,这两棵树的节点通过alternate相连

上面有提到workInProgress Fiber,创建workInProgress Fiber发生在createWorkInProgress()函数中,会创建复用Fiber

修改current Fiber的指向,是通过修改FiberRootNode.current来进行切换

// ReactFiberWorkLoop.old.js
root.current = finishedWork;

mount

第一次渲染时,会创建 fiberRoot 和 rootFiber,然后根据jsx对象创建Fiber节点,节点连接成current Fiber树

该阶段流程分3步

  • 第一步:刚开始只创建了fiberRoot和rootFiber两个节点

1.png

  • 第二步:根据jsx创建workInProgress Fiber

2.png

  • 第三步:把workInProgress Fiber切换成current Fiber

3.png

update

update分为2步

  • 第一步:根据current Fiber创建workProgress Fiber

update1.png

  • 第二步:把workInProgress Fiber切换成current Fiber

update2.png

Q & A

函数组件为什么能保存状态?

  • 状态并不是保存在函数组件里面,而是保存在Fiber身上
  • Fiber身上有updateQueue,可以通过循环updateQueque计算新的状态

为什么可以实现中断和继续?

因为中断的时候会保存下一个Fiber,恢复的时候,会拿到这个所谓的下一个Fiber,继续遍历