banner
banner
banner
NEWS LETTER

React-协调算法|背后的核心算法React Fiber

Scroll down
  • React 核心机制是跟踪组件的状态变化,并将更新的状态映射到新的界面,这个过程称为协调。
  • React Fiber 是React核心算法的一次重新实现

从问题来理解为什么react需要Fiber

Q1:React的设计理念是构建快速响应的大型Web应用程序。制约快速响应的因素?反映算法不足

  • CPU瓶颈:大量的同步计算任务阻塞了浏览器的UI渲染,造成页面卡顿。而 React 的 Reconcilation 是 CPU 密集型的操作,React 15使用的Virtual DOM协调算法(因为它在内部使用堆栈也被称为堆栈协调器),共享一个协调器,可能会导致混淆。
  • IO的瓶颈:网络请求响应不及时,造成白屏

Q2:Vue3 动静结合的DOM Diff 与 React的对比?反映编译时优化不足

  • Vue3的DOM Diff在预编译进行优化,在预编译阶段静态分析模版,分析出视图依赖了哪些数据,进行响应式处理。Template 模板是一种非常有约束的语言,你只能以某种方式去编写模板。
  • 而React 是局部渲染,无法从模板层面进行静态分析。JSX 具有 JavaScript 的完整表现力,可以构建非常复杂的组件。但是灵活的语法,也意味着引擎难以理解,无法预判开发者的用户意图,从而难以优化性能。

React 架构

  • 调度器(Scheduler):调度任务优先级,高级任务优先进入协调器,不使用requestIdleCallback(兼容性不好)。
  • 协调器(Reconciler):负责找出更改的组件,更新工作从递归变成可以中断的循环过程。内部采用Fiber架构
  • 渲染器(Renderer):将变化的组件渲染到页面上

源码地址

调度器

  • 以浏览器是否有剩余时间作为任务中断的标准,当浏览器有剩余时间时通知我们。

协调器

  • 更新工作从15递归变成了16+可以中断的循环过程。每次循环都会调用shouldYield判断当前是否有剩余时间。
  • 当Scheduler将任务交给Reconciler后,Reconciler会为变化的虚拟 DOM 打上代表增/删/更新的标记
  • 整个Scheduler与Reconciler的工作都在内存中进行。只有当所有组件都完成Reconciler的工作,才会统一交给Renderer。

渲染器

  • 根据Reconciler为虚拟 DOM 打的标记,同步执行对应的 DOM 操作

Fiber

  • 本质是『虚拟DOM』。Fiber节点可以保存对应的DOM节点,Fiber节点构成的Fiber树就对应DOM树
  • 从数据结构看,每个Fiber节点对应一个React Element,保存了该组件的类型(函数组件、类组件、原生组件)、对应的DOM节点等信息
  • 从工作单元看,每个Fiber节点保存了本次更新中该组件改变的状态、要执行的工作(删除、插入、更新)

Fiber 树

  • 特点:链表结构,将协调的『递归遍历』改为『循环遍历』
  • React 第一次渲染时,会通过 React.createElement 创建一颗 Element 树,可以称之为 Virtual DOM Tree,由于要记录上下文信息,加入了 Fiber,每一个 Element 会对应一个 Fiber Node,将 Fiber Node 链接起来的结构成为 Fiber Tree。
  • 多个节点如何连接成树?
    1
    2
    3
    this.return = null  // 指向父Fiber节点
    this.child = null // 指向子Fiber节点
    this.sibling = null // 指向右边第一个兄弟Fiber节点

Fiber 架构的工作原理

  • 双缓存:在内存中构建并直接替换
  • 双缓存树:在React中最多会同时存在两棵Fiber树。
    • 当前屏幕显示内容对应的Fiber树称为current Fiber树(即上次渲染构建的Fiber树)。
    • 正在内存中构建的Fiber树称为workInProgress Fiber树。(无论是创建还是更新、挂起、恢复、终止操作都是发生在W树创建过程中。W树构建过程其实就是循环的执行任务和创建下一个任务。

初次渲染的构建流程

  1. 创建阶段:首次执行ReactDOM.render 会创建 fiberRootNode(整个应用的根节点)和 rootFiber(<App />所在组件树的根节点)。由于首屏渲染,页面没有挂载任何DOM,所以fiberRootNode.current指向的rootFiber没有任何子Fiber节点(即current Fiber树为空)。
  2. render阶段:进入render阶段,根据组件返回的JSX载内存中一次创建Fiber节点并连接在一起构建Fiber树,被称为『workInProgress Fiber树』。在构建W树时会尝试复用C树中已有的Fiber节点内的属性。在首屏渲染时,只有rootFiber存在对应的current Fiber(即rootFiber.alternate)
  3. commit阶段:进入commit阶段,已构建完的workInProgress Fiber树渲染到页面上

更新阶段的构建流程

  1. 开启新一轮的render阶段并构建新一颗W树。创建时复用C树的对应节点数据
  2. 已构建完的W树渲染到页面上。渲染完成W树变成C树。

JSX & Fiber节点

  • 在组件mount时,Reconciler『根据JSX描述』的组件内容生成组件对应的『Fiber节点』。
  • 在update时,Reconciler将JSX与Fiber节点保存的『数据对比』,生成组件对应的Fiber节点,并根据对比结果为Fiber节点打上标记。

React Fiber 执行过程

任务拆分

  • 在调和阶段递归遍历 VDOM 这个大任务分成若干小任务,每个任务只负责一个节点的处理。

任务挂起、恢复、终止

  • 当当前分配的任务完成后,先判断这一帧是否还有空闲时间,没有就挂起下一个任务的执行,记住当前挂起的节点,让出控制权给浏览器执行更高优先级任务

任务恢复

  • 在浏览器渲染完一帧后,判断当前帧是否有剩余时间,如果有就恢复执行之前挂起的任务。如果没有任务需要处理,代表调和阶段完成,可以开始进入渲染阶段。
  • 判断一帧是否用空闲时间,使用 RequestIdleCallback
  • 恢复执行时通过链表获取下一个任务

任务终止

  • 不是每次更新都会走到提交阶段。当在调和过程中触发了新的更新,在执行下一个任务的时候,判断是否有优先级更高的执行任务,如果有就终止原来将要执行的任务,开始新的 workInProgressFiber 树构建过程,开始新的更新流程。这样可以避免重复更新操作。这也是在 React 16 以后生命周期函数 componentWillMount 有可能会执行多次的原因。

任务具备优先级

  • 在任务执行过程中收集到的每个FiberNode的副作用,形成的副作用链表。到commit阶段时,直接遍历副作用链完成DOM更新。更新DOM的过程不可中断。
  • 任务优先级有六种
    • synchronous:与之前的堆栈协调器操作一样,同步执行
    • task:在next tick之前执行
    • animation:下一帧之前执行
    • high:在不久的将来立即执行
    • low:稍微延迟执行也没关系
    • offscreen:下一次render 或scroll 时执行
其他文章
cover
React-组件通信方式
  • 25/06/24
  • 14:45
  • React
cover
7种强大的JavaScript继承技术
  • 25/03/19
  • 10:56
  • JavaScript
目录导航 置顶
  1. 1. 从问题来理解为什么react需要Fiber
    1. 1.1. Q1:React的设计理念是构建快速响应的大型Web应用程序。制约快速响应的因素?反映算法不足
    2. 1.2. Q2:Vue3 动静结合的DOM Diff 与 React的对比?反映编译时优化不足
  2. 2. React 架构
    1. 2.1. 调度器
    2. 2.2. 协调器
    3. 2.3. 渲染器
  3. 3. Fiber
    1. 3.1. Fiber 树
  4. 4. Fiber 架构的工作原理
    1. 4.1. 初次渲染的构建流程
    2. 4.2. 更新阶段的构建流程
    3. 4.3. JSX & Fiber节点
  5. 5. React Fiber 执行过程
    1. 5.1. 任务拆分
    2. 5.2. 任务挂起、恢复、终止
    3. 5.3. 任务恢复
    4. 5.4. 任务终止
    5. 5.5. 任务具备优先级
请输入关键词进行搜索