一、了解React Fiber
1、Fiber
在计算机科学中,Fiber是指比线程更纤细的一个过程,即纤程;纤程的出现意在对渲染过程实现更加精细的控制;
2、React Fiber产生的背景
在浏览器中,JS引擎与页面渲染引擎这两个线程是互斥的,当其中一个线程执行时,另一个线程只能挂起。
在React15版本中,React采用的协调方式是Stack Reconciler,Stack Reconciler使用递归的方式创建虚拟DOM,递归的过程是不能中断的,并且所有的任务都被视为同步任务;若需要渲染的组件比较庞大,递归过程的时间就会比较久,也就会长时间占据主线程,渲染引擎就得长时间等待,界面就会长时间不更新,用户交互、布局、渲染就会停止,从而造成页面卡顿。
3、React Fiber
React Fiber是React框架的一种底层架构,是React16引入的一种新的核心算法,在React中,Fiber就是一种数据结构,Fiber对象用于存储与组件相关的数据信息。
(1)Fiber对象的数据结构
function FiberNode(
this: $FlowFixMe,
tag: WorkTag,
pendingProps: mixed,
key: null | string,
mode: TypeOfMode,
) {
// Fiber元素的静态属性相关
this.tag = tag; // 根据tag创建不同的节点类型
this.key = key; // fiber的key属性,用于区别是否更新
this.elementType = null;
this.type = null; // fiber对应的DOM元素的标签类型
this.stateNode = null; // Fiber节点的实例,在类组件场景下,是组件的类;HostComponent场景,是DOM元素
// Fiber
this.return = null; // 指向父级fiber
this.child = null; // 指向子fiber
this.sibling = null; // 同级兄弟fiber
this.index = 0;
this.ref = null; // ref相关
this.refCleanup = null;
// 作为工作单元与Fiber更新相关
this.pendingProps = pendingProps; // 刚开始工作阶段的 props
this.memoizedProps = null; // 工作结束时确定下来的 props
this.memoizedState = null; // 更新完成后的新 state 缓存的状态,类组件存储Fiber节点的状态,函数组件存储的是hooks链表
this.updateQueue = null; // Fiber产生的更新操作都会放在更新队列中:用于存储组件状态的更新操作,包括对状态的更新、副作用的执行等
this.dependencies = null;
this.mode = mode;
// Effects
this.flags = NoFlags; // 比如插入 更改 删除dom等 初始状态时表示没有任何标记(因为还没进行fiberNode对比)
this.subtreeFlags = NoFlags; // 子节点副作用标识
this.deletions = null; // 用于存放被删除的子节点
// 旧的优先级
// this.expirationTime = NoWork;
// this.childExpirationTime = NoWork;
// 新的优先级:判断是否需要更新
this.lanes = NoLanes; // 该fiber中的优先级,它可以判断当前节点是否需要更新
this.childLanes = NoLanes;// 子树中的优先级,它可以判断当前节点的子树是否需要更新
/**
* 可以看成是workInProgress(或current)树中的和它一样的节点
* 可以通过这个字段是否为null判断当前这个fiber处在更新还是创建过程
*/
this.alternate = null;
}
(2)目标
React Fiber的目标是实现虚拟DOM的增量渲染,将更新任务拆分为小的、可中断的单元,使React在更新时可以更灵活地控制和切换任务,提高应用的响应性。
(3)特性
- 时间分片:将长时间运行的任务拆分为多个短时间的子任务,并且在不同的时间点执行;
- 更新优先级:保证高优先级的任务优先执行,可以插队;
- 双缓存机制;
- 可以暂停、中止、复用渲染任务;
二、深入React Fiber原理
1、原理
为了解决Stack Reconciler造成的页面卡顿的问题,引入了React Fiber核心算法;React Fiber与Scheduler出色的调度模式可以实现异步可中断的更新行为;
React Fiber引入了时间切片的概念,可以将渲染更新的过程拆分为多个子任务,每次只执行一小部分,将任务的执行时间控制在一定范围内,在任务执行完成后查看是否还有剩余时间,如果有,则继续执行下一个任务;否则,挂起当前任务,优先处理优先级更高的任务,将时间控制权交给主线程;等到主线程空闲的时候继续执行任务;即:实现任务的中断与恢复。
React Fiber还给每个任务提供了优先级,在调度阶段根据优先级给任务进行排序,使得优先级更高的任务优先执行;
React Fiber使用双缓存来完成fiber树的构建和替换,对应着DOM树的创建与更新;
2、双缓存机制
双缓存机制设置的目的是为了更快地渲染DOM;
Fiber架构中存在两棵树,一棵是真实UI对应的Fiber Tree,即Current Tree;另一棵是正在内存中构建的Fiber Tree,即WorkInProgress Tree;这两棵树通过alternate属性进行关联,每次更新完成后,会将workInProgress赋值给current;
在新 workInProgress tree 的创建过程中,会同 currentFiber 的对应节点进行 Diff 比较,收集副作用;同时也会复用和 currentFiber 对应的节点对象,减少新创建对象带来的开销。
workInProgress tree 构建过程其实就是循环的执行任务和创建下一个任务;当没有下一个任务需要执行的时候,workInProgress Tree构建完成,开始进入提交阶段,完成真实DOM更新;
3、React Fiber的工作流程
- ReactDOM.render调用时,开始创建或更新Fiber树;
- 从根节点开始先请求调度,将创建的更新任务添加到任务队列中,等待调度;
- 调度由scheduler模块完成,其核心职责就是时间分片、执行回调;
- 从根节点开始遍历Fiber,并且构建workInProgress Tree,即进入reconciliation阶段;
- React会构建两棵树,一棵是代表当前状态的currentTree,另一棵是待更新的workInProgress Tree;
- 遍历current Tree,重用或更新Fiber Node到workInProgress Tree;
- 每更新一个节点,同时会生成该节点对应的Effect List;
- 根据Effect List更新DOM,即commit阶段;
- React会遍历Effect List将所有变更一次性更新到DOM上;
- 该过程不可中断,必须一直执行直到更新完成;
三、React Fiber源码基础
1、fiberRoot与rootFiber
(1)fiberRoot
fiberRoot:表示数据结构中的最外层对象,是Fiber数据结构中的根节点;
fiberRoot包含rootFiber对象,fiberRoot的current属性指向rootFiber;
该对象包含应用中的所有组件和状态,并且能够记录应用的更新信息;在React Fiber中,fiberRoot是唯一的,用于表示整个应用的状态和挂载点。
【FiberRoot的数据结构如下】
function FiberRootNode(
containerInfo,
tag,
hydrate, // 表示是否是服务端渲染
) {
// 表示React的工作模式:取值有ConcurrentRoot - 并发模式、LegacyRoot - 同步模式
this.tag = tag;
this.containerInfo = containerInfo; // 表示root节点信息
this.pendingChildren = null;
this.current = null;
this.pingCache = null;
this.finishedWork = null;
this.timeoutHandle = noTimeout;
this.context = null;
this.pendingContext = null;
this.callbackNode = null;
this.callbackPriority = NoLane;
this.eventTimes = createLaneMap(NoLanes);
this.expirationTimes = createLaneMap(NoTimestamp);
this.pendingLanes = NoLanes;
this.suspendedLanes = NoLanes;
this.pingedLanes = NoLanes;
this.expiredLanes = NoLanes;
this.mutableReadLanes = NoLanes;
this.finishedLanes = NoLanes;
this.entangledLanes = NoLanes;
this.entanglements = createLaneMap(NoLanes);
this.identifierPrefix = identifierPrefix;
this.onRecoverableError = onRecoverableError;
}
(2)rootFiber
rootFiber表示组件挂载点对应的Fiber对象,如:示例中对应的就是id为root的div标签。
rootFiber指向fiberRoot,并在其对象中有一个stateNode属性,指向fiberRoot。在React Fiber中,rootFiber可以有多个,因为render方法是可以调用多次的。
总结
React Fiber是React16引入的一种新的核心算法,用于改进应用程序的渲染性能和响应性;Fiber引入了时间切片与优先级的概念,将渲染工作拆分成多个较小的任务单元,可以更好地管理渲染优先级、中断与恢复,并使得React能够更有效地利用浏览器的主线程。