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首屏渲染
咱们换一个简略的示例,来更好的了解渲染流程:
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树更新
在咱们点击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同步挪动。