当前位置: 首页 > 科技观察

一篇带你了解什么是ReactFiber的文章?

时间:2023-03-13 18:07:18 科技观察

大家好,我是前端西瓜哥。为了提升React的性能,React团队在开发React16时做了一次底层重构,引入了ReactFiber的概念。什么是反应纤维?Fiber,原意为“光纤”,在计算机世界中就是“光纤”的意思。Fiber可以看作协程的一种,是一种任务调度的方式。JavaScript是单线程的,有事件循环的概念。它有一个有优先级的任务队列,只能按顺序执行一个任务。它不支持同时执行多个任务。这样设计的好处是不需要考虑多线程带来的顺序问题,为锁做一些额外的逻辑来保证执行顺序符合预期。但是由于无法使用并行能力,在CPU密集型场景下会出现性能问题。例如,一个任务耗时过长,会引起其他任务,导致用户交互响应延迟。React组件更新是一个CPU密集型操作,因为需要比较新旧虚拟DOM树(diff,React中的Reconciliation负责),找出需要更新的内容(patch),更新真正的通过打补丁的DOM树(React中的渲染器负责)。当需要比较的组件树很多时,会发生大量的新旧节点比较,CPU会占用大量时间。当耗时超过16.6ms(每秒60帧的基准)时,用户会感觉到明显的卡顿。这一系列操作是递归实现的,是同步的,不可中断的。因为一旦中断,调用栈就会被破坏,中间状态就会丢失。这种基于调用堆栈的实现称为StackReconciliation。React16的一项关键任务是在更新组件时优化大量CPU计算。最后,我们选择了使用“时间分片”的方案,即将应该在同一时间完成的工作拆分成异步任务。执行的时间。这种新架构称为FiberReconciliation。在React中,Fiber模拟了之前的递归调用,具体是通过一个链表来模拟函数的调用栈,使得调用可以中断,将一个大的更新任务拆分成小任务,并设置优先级。在浏览器空闲时异步执行。在FiberNode之前,我们提到过链表的遍历是用来模拟递归栈调用的,而链表的节点React就是用FiberNode来表示的。FiberNode其实是一个虚拟的DOM,记录了:节点相关的类型,比如tag表示组件类型,type表示元素类型等。节点的指向。与副作用相关的属性。车道是关于调度优先级的。functionFiberNode(tag:WorkTag,pendingProps:mixed,key:null|string,mode:TypeOfMode,){//实例this.tag=tag;//组件类型,例如Function/Class/Hostthis.key=key;//key唯一值,通常在列表中使用this.elementType=null;this.type=null;//元素类型,字符串或者类或者函数,比如"div"/ComponentFn/Classthis.stateNode=null;//指向真正的DOM对象//Fiberthis.return=null;//父光纤this.child=null;//孩子的第一个this.siblingFiber=null;//下一个兄弟节点this.index=0;//在兄弟节点中的位置this.ref=null;this.pendingProps=pendingProps;this.memoizedProps=null;this.updateQueue=null;this.memoizedState=null;this.dependencies=null;this.mode=模式;//效果this.flags=NoFlags;this.subtreeFlags=NoFlags;这个。删除=空;this.lanes=NoLanes;this.childLanes=NoLanes;this.alternate=null;//...}Fiberviareturn指向父Fiber,child指向子Fiber的头部,sibling指向下一个兄弟节点。通过它们我们其实可以得到一个完整的结构树。for:functionApp(){return(hello,Fiber

);}形成的Fiber树为:其中圆弧为调用序列。紫色是beginWork,粉色是completeWork。beginWork是一个“通过”过程,而completeWork是一个“返回”过程。为什么不使用生成器或异步/等待?Generator和async/await也可以在函数中途挂起函数执行的逻辑,放弃执行,同步变异步。但React并没有选择它们,因为:它们具有传染性。例如,如果一个函数使用了async,那么调用它的函数必须添加async,这会产生语法开销和额外的性能开销。无法恢复生成器和异步/等待中的某些中间状态。详情见官方github问题讨论:https://github.com/facebook/react/issues/7942#issuecomment-254987818。Scheduler做时间分片,拆分多个任务,React可以以此为基石,为任务设置优先级。React实现了一个Scheduler(调度器)来实现任务调度执行,并将其独立成一个单独的包,在浏览器空闲时执行。其实浏览器也提供了requestIdleCallbackAPI来支持这个能力,但是兼容性确实不好,React还是自己实现了一套。这个调度器支持优先级。底层使用一个小的顶堆来保证最快的过期任务能够被高效的获取然后执行。小顶堆其实就是一个优先级队列。小顶堆在结构上是一棵完全二叉树,但是可以保证每次从堆顶取出一个元素,都是最小的元素。任务有几个优先级:NoPriority:无优先级。ImmediatePriority:立即执行。UserBlockingPriority:用户阻塞优先级,如果不执行,可能会导致用户交互阻塞。NormalPriority:正常优先级。LowPriority:低优先级。IdlePriority:空闲优先级。React本身也有一个优先级,叫做Lane,这是不一样的。说到底,React的架构实在是太宏大了,今天就随便说说吧。总的来说,ReactFiber是React16引入的新架构,将原来的同步不可中断更新变成了异步可中断更新,将原来的耗时大任务拆分成时间片。小任务在浏览器空闲时执行。另外,加入了优先级的概念,优先执行一些重要的任务,比如一些用户交互响应功能。一切为了更好的用户体验。