浅谈Fiber架构的工作流程

白胜
• 阅读 1875

Fiber 起源

Fiber架构诞生于React16,是为了解决React15及之前版本的更新不可中断问题的。

堆栈协调器 Stack Reconciler

我们知道,React在工作的时候中有一个重要的阶段叫做协调阶段Reconcile,在React15的时候,React采用的还是堆栈协调Stack Reconciler,之所以把它成为堆栈协调,是因为React是使用递归来构建虚拟Dom树(React 15的叫法)的,构建过程中,数据被保存在递归调用栈中。由于递归是同步执行的,所以它一旦执行就只能执行完,不能被中途打断。这导致浏览器在执行代码时,Stack Reconciler 经常由于需要协调非常多的节点而耗费大量时间,而浏览器的UI渲染工作迟迟得不到执行,这会导致浏览器产生肉眼可见的掉帧现象。

Fiber协调器 Fiber Reconciler

为了解决Stack Reconciler的递归调用,不可中断问题,React团队在React16发布时推出了全新的Fiber架构,旨在解决老版本的更新不可中断问题。React团队提出了一种新的模式**Concurrent Mode**,一个大的同步任务可以分成许多小的同步任务,在浏览器运作的时候,平均的把这些小的同步任务塞到每一帧的一小块时间里执行,这种做法我们称为可中断的异步更新。而我们知道,在React15的时候,同步任务由于架构的限制,是不可切分的,一旦暂停任务只能全部中断。但是权限的Fiber架构可以保存更新时的运行状态,以便下次调用时可以继续上次的更新。所以说,Fiber架构为Concurrent Mode的推行打下了基础,这种可中断的更新解决了卡顿掉帧的问题,也带给了用户更好的交互体验。

Fiber工作流程

在React15中我们知道有虚拟DOM树,用来建立和真实DOM的映射关系,在Fiber架构中我们把种映射成为Fiber树。一棵Fiber由一个当前应用根节点FiberRootNode和当前组件树根节点rootFiber构成,rootFiber实际上是一个FiberNode,它又连接了由其他FiberNode组成的子树。FiberRootNode通过current指针连接当前组件树rootFiber。这里我们用了当前组件树这个词,其实是为了引出Fiber架构下的双缓存机制。

双缓存机制

我们在图像处理的时候,往往会经历渲染画面-清除画面-重新渲染画面这个过程,往往清除画面后进行重绘的时候,可能会比较耗时,这时候用户就会感知到闪屏的现象。如果我们在内存中进行当前帧画面的构建,构建完毕后直接替换之前的画面,省去清屏的步骤,这样就节省了很多时间,很大程度上改善了用户体验。
所以在React中,我们也使用了双缓存机制,即系统中始终存在着两棵Fiber树,一棵对应的是当前DOM在屏幕上显示的画面,被称作current,此时我们称其为当前组件树,一棵是在内存中进行构建的新的Fiber树,被称作workInProgress,此时我们称其为正在构建中的组件树

Fiber树示例

在如下的代码所渲染的组件中

function App() {
  return (
    <div className="App">
      <header>
        <div>
            Hello React
        </div>
        <section>
          Happy Hacking
        </section>
      </header>
    </div>
  );
}

一棵完整的Fiber树示例如图所示:
根节点FiberRootNode会使用current指向当前组件树,当前组件树的根节点rootFIber会使用child指向子节点,如果存在多个子节点,那么子节点与子节点之间又会使用sibling指针连接。
浅谈Fiber架构的工作流程

Fiber首屏渲染

我们换一个简单的示例,来更好的理解渲染流程:

function App() {
  const [num , setNum] = useState(0);
  return (
        <p onClick={()=>setNum(num + 1)}>
           {num}
        </p>
  );
}

在一开始,React会先建立FiberRootNode和rootFiber作为初始的Fiber树,FiberRootNode的current指针指向rootFiber,此时rootFiber是为空的。然后根据组件树返回的jsx对象(就是createElement的返回值对象)在render阶段创建新的rootFiber,这一步是递归的创建workInProgress,创建完workInProgress后,然后在commit阶段把这棵树渲染到页面上,此时修改current指针指向workInProgress,使其成为新的current树。这就是Fiber的首屏渲染流程。current和workInProgress通过alternate互相连接,我们后面会讲到为何这么做。
浅谈Fiber架构的工作流程

Fiber树更新

在我们点击p使得页面触发更新后,React会在内存中重新构建一棵完整的Fiber树,也就是workInProgress,在构建完成后会直接让current指针指向它,然后render阶段就会基于这个新的current进行渲染。在此过程中我们可以使用Diff算法决定是否复用current树中的节点,省去创建节点的流程,进一步加快渲染过程。

节点复用

前面我们说过了,在页面更新时,由于React的双缓存机制,在渲染页面的时候,会先从内存中构建一棵Fiber树,等构建完毕后,直接改变current指针的指向替换掉当前的Fiber树,达到页面更新的目的。所以在构建workInProgress树的时候,我们实际上还有一棵current树,由于大多数更新不过是某个样式的改变或数据小规模更新,导致UI变化不是很大,如果此时我们还在内存中重新的从无到有渲染一棵完整的Fiber树,是很耗时的,所以我们可以基于current 树来复用一些节点创建workInProgress树,我们会使用Diff算法(有兴趣的小伙伴可以自行学习)来决定是否复用节点,要复用的节点就是current.alternate。

有小伙伴可能现在就要问了,current.alternate不是一棵完整的树吗,怎么可以直接复用呢?其实在构建workInProgress时,current也在不断的变化,和workInProgress同步移动。

浅谈Fiber架构的工作流程

点赞
收藏
评论区
推荐文章
blmius blmius
4年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
美凌格栋栋酱 美凌格栋栋酱
7个月前
Oracle 分组与拼接字符串同时使用
SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Chase620 Chase620
4年前
从中断机制看 React Fiber 技术
带你了解计算机的中断机制(操作系统心脏)是如何提在ReactFiber中应用及提高了页面渲染性能和用户体验。前言React16开始,采用了Fiber机制替代了原有的同步渲染VDOM的方案,提高了页面渲染性能和用户体验。Fiber究竟是什么,网上也有很多优秀的技术揭秘文章,本篇主要想从计算机的中断机制来聊聊ReactFiber技术
Stella981 Stella981
3年前
React 架构的演变
前面的文章分析了Concurrent模式下异步更新的逻辑,以及Fiber架构是如何进行时间分片的,更新过程中的很多内容都省略了,评论区也收到了一些同学对更新过程的疑惑,今天的文章就来讲解下ReactFiber架构的更新机制。Fiber数据结构我们先回顾一下Fiber节点的数据结构(之前文章省略了一部分属性,所
Wesley13 Wesley13
3年前
VBox 启动虚拟机失败
在Vbox(5.0.8版本)启动Ubuntu的虚拟机时,遇到错误信息:NtCreateFile(\\Device\\VBoxDrvStub)failed:0xc000000034STATUS\_OBJECT\_NAME\_NOT\_FOUND(0retries) (rc101)Makesurethekern
Wesley13 Wesley13
3年前
FLV文件格式
1.        FLV文件对齐方式FLV文件以大端对齐方式存放多字节整型。如存放数字无符号16位的数字300(0x012C),那么在FLV文件中存放的顺序是:|0x01|0x2C|。如果是无符号32位数字300(0x0000012C),那么在FLV文件中的存放顺序是:|0x00|0x00|0x00|0x01|0x2C。2.  
Wesley13 Wesley13
3年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
3年前
PHP创建多级树型结构
<!lang:php<?php$areaarray(array('id'1,'pid'0,'name''中国'),array('id'5,'pid'0,'name''美国'),array('id'2,'pid'1,'name''吉林'),array('id'4,'pid'2,'n
Wesley13 Wesley13
3年前
Java日期时间API系列36
  十二时辰,古代劳动人民把一昼夜划分成十二个时段,每一个时段叫一个时辰。二十四小时和十二时辰对照表:时辰时间24时制子时深夜11:00凌晨01:0023:0001:00丑时上午01:00上午03:0001:0003:00寅时上午03:00上午0
Stella981 Stella981
3年前
Atomikos事务恢复流程源码解析
AtomikosXA事务恢复说事务恢复流程之前,我们来讨论下,会啥会出现事务恢复?XA二阶段提交协议不是强一致性的吗?要解答这个问题,我们就要来看看XA二阶段协议有什么问题?问题一:单点故障由于协调者的重要性,一旦协调者TM发生故障。参与者RM会一直阻塞下去。尤其在第二阶段,协调者发生故障,那么所有的参与者还都处于锁定事
白胜
白胜
Lv1
不是我不想找你,只是我觉得你不需要我。
文章
4
粉丝
0
获赞
0