浅谈React Fiber工作原理

文章详细介绍了React中的Fiber数据结构,包括Fiber节点的属性和它们如何构成工作单元。Fiber允许React拆分任务,优化性能,并使用双缓存策略避免闪烁。在挂载和更新阶段,Fiber树如何构建和切换也进行了阐述。
摘要由CSDN通过智能技术生成

静态数据结构

每个 Fiber 节点对应一个组件,保存了该组件的类型,对应的 DOM 节点的对应信息。

FiberRootNode 有且仅有一个,而 rootFiber 可以有多个,因为我们可以挂载多个应用(也就是多次调用ReactDOM.render

function App(props) {
  const [age, setAge] = useState(24);
  return (
    <div>
      chenjiang
      {age}
    </div>
  );
}

ReactDOM.render(<App />, document.getElementById("root"));

最终形成数据结构如下:

请添加图片描述

Fiber 的数据结构

function FiberNode(tag: WorkTag, pendingProps: mixed, key: null | string, mode: TypeOfMode) {
  // Instance

  // 组件的类型 FunctionComponent、classComponent、HostComponent(指的是DOM节点对应的Fiber节点)
  this.tag = tag;

  this.key = key;

  // 大多数情况下于tag相同,使用React.memo包裹时候,elementType和tag不同
  this.elementType = null;

  // 对于FunctionComponent,指函数本身
  // 对于ClassComponent,指class
  // 对于HostComponet,指的是DOM节点tagName
  this.type = null;

  // 对于HostComponent来说指的是对应的真实的DOM节点
  this.stateNode = null;

  // 以下属性用于连接其他Fiber节点形成Fiber树。

  // 指向父fiber节点
  this.return = null;

  // 指向第一个子fiber节点
  this.child = null;

  // 指向第一个兄弟fiber节点
  this.sibling = null;
  this.index = 0;

  // ref属性
  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;
}

动态的工作单元

Fiber 可以理解成一个工作单元,每次执行完一个工作单元,react 就会检查还剩余多少时间,如果没有就把控制权交还给浏览器。React Fiber 与浏览器的核心交互过程如下:

undefined

首先 React 向浏览器请求调度,浏览器在一帧中如果还有空闲时间,会去判断是否存在待执行任务,不存在就直接将控制权交给浏览器,如果存在就会执行对应的任务,执行完成后会判断是否还有时间,有时间且有待执行任务则会继续执行下一个任务,否则就会将控制权交给浏览器。

Fiber 可以被理解为划分一个个更小的工作单元,它是把一个大任务拆分为了很多个小块任务,一个小块任务的执行必须是一次完成的,不能出现暂停,但是一个小块任务执行完后可以移交控制权给浏览器去响应用户,从而不用等待大任务一直执行完成再去响应用户。

架构(双缓存工作机制)

什么是双缓存

当我们用canvas绘制动画,每一帧绘制前都会调用ctx.clearRect清除上一帧的画面。如果当前帧画面计算量比较大,导致清除上一帧画面到绘制当前帧画面之间有较长间隙,就会出现白屏闪烁。

为了解决这个问题,我们可以在内存中绘制当前帧动画,绘制完毕后直接用当前帧替换上一帧画面,由于省去了两帧替换间的计算时间,不会出现从白屏到出现画面的闪烁情况。

这种在内存中构建并直接替换的技术叫做双缓存

React使用“双缓存”来完成Fiber树的构建与替换——对应着DOM树的创建与更新。

双缓存 Fiber 树

React 中存在两棵树,一颗为current Fiber tree,另外一颗为workInProgress Fiber tree

  • current Fiber tree也就是当前页面中显示内容对应的 Fiber 树,每个 Fiber 节点成为 current fiber。
  • workInProgress Fiber tree正在内存中构建的 Fiber 树。

两颗树的 fiber 节点是通过alternate属性进行连接的。

currentFiber.alternate === workInProgressFiber;
workInProgressFiber.alternate === currentFiber;

React 的根节点(FiberRootNode)通过 current 指针在不同的 Fiber 树的 rootFiber 间切换来实现 Fiber 树的切换。

请添加图片描述

Fiber 树的构建(挂载阶段、更新阶段)

再次强调:FiberRootNode 有且仅有一个,而 rootFiber 可以有多个,因为我们可以挂载多个应用(也就是多次调用ReactDOM.render

以下代码为例:

function App(props) {
  const [age, setAge] = useState(24);
  return (
    <div
      onClick={() => {
        setAge(25);
      }}
    >
      {age}
    </div>
  );
}

ReactDOM.render(<App />, document.getElementById("root"));

挂载阶段(mount 阶段)

首次调用ReactDOM.render就会创建FiberRootNode,每次调用ReactDOM.render就会创建当前应用的根节点RootFiber,由于在首屏渲染之前页面是空白的,因此RootFiber不存在子节点。

请添加图片描述

挂载阶段流程如下(首屏渲染)

首先创建 fiber 树的根节点RootFiber,在两棵树之间都存在的 Fiber 节点用alternate连接,接下来采用深度遍历的方式创建 Fiber 树,当workInProgress Fiber tree完成了渲染,最后 FiberRootNode 的 current 指针就指向workInProgress Fiber tree的根节点,此时就变成current Fiber tree

请添加图片描述

更新阶段 (update 阶段)

每次触发更新都会重新创建一颗workInProgress Fiber Treecurrent Fiber与本次更新返回的JSX做对比,生成workInProgress Fiber的过程就是 diff 算法,update 阶段与 mount 阶段,最大的区别就是是否有 diff 算法。
请添加图片描述

参考文章:

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值