伴随着React 16的发布,一个全新的名词出现在了人们的眼前——Fiber
,当我们复制这个单词到谷歌翻译上,可以看到它的中文解释:纤维,那么究竟什么是Fiber
,而且React团队又为何大费周章地来重写React架构?
这里在正式阅读源码前,总结两个问题:
1、什么是Fiber?
2、Fiber带来了哪些益处 OR 为什么要创造fiber?
先来看一张GIF图,Stack Example 是不感觉不够丝滑流畅,甚至卡顿现象很明显,造成这种现象的原因是什么呢?那就要从React 15的架构来说了,React 15架构可以分为两层,Reconciler(协调器)
和Renderer(渲染器)
这两个部下干了什么事情呢?Reconciler
负责找出变化的组件,Renderer
负责将变化的组件渲染到页面上,一个负责协调,一个负责渲染,当页面上有更新时,Reconciler
先上,Renderer
后上,同步更新,是不是看起来很完善呢?
问题出现在了Reconciler
层,为了与React 16及之后的Fiber Reconciler
区分,React团队将React 15的Reconciler
层 称为 Stack Reconcile
,顾名思义,Stack Reconcile
采用的是栈的数据结构,并且在实现上采用的是递归的算法,这就带来了一些弊端,一旦执行,就必须一条路走到黑,大有不撞南墙不回头的气势,如果虚拟DOM树层数有10W层,需要进行增删改的节点有足够多,毫无疑问,在Reconciler
层会耗费大量时间。
回到上述的GIF图,当触发页面更新的事件产生了,页面内部开始协调计算,但由于数据量过大,导致卡在了Reconciler
层的时间被用户所感知到,才进入了Renderer
层进行渲染。并且这只是单纯的页面渲染,如果此时有个Input框,需求进行模糊搜索,那么很有可能用户在输入了内容之后,等了一会儿才连续弹出匹配到的列表,这不符合React为了践行“构建快速响应
的大型 Web 应用程序”理念,于是React团队决定进行重构。
既然递归调用
的栈
结构是不能被打断的,那么是否可以使用一种新的数据结构,并且设定一个短时间,如果在规定时间内不能完成协调任务,就转到页面渲染阶段,然后在回来继续协调计算,以此往复,这样如果协调的时间足够短,用户是无感知的,而且还可以应对层数足够多的DOM树和繁多的更新操作。
于是Fiber便被创造了出来,为了满足上述的条件,Fiber使用了链表
数据结构,来看一下使用了Fiber结构之后页面更新的GIF Fiber Example 是不是比第一张GIF要流畅很多了呢?来解答一个问题,Fiber
是一种链表
的数据结构,本质上是React团队重写的虚拟DOM,而使用链表的原因是:链表有指针去从高优先级指向低优先级,可以随时中断,然后还可以在上次离开的地方重新开始
。
第二个问题的答案是对本篇文章的概括,react 15及之前依赖的架构是深度递归
的过程,其中这个过程在未完成前,js 一直占据浏览器主线程,导致其他用户响应的事件线程和ui更新线程都不能响应,Fiber可以实现异步
和增量渲染
,满足快速响应
和提升用户体验
的设计理念。
2021-11-30更新
了解了Fiber的产生后,来总结一下Fiber的含义:
- 作为架构来说,之前React15的
Reconciler
采用递归的方式执行,数据保存在递归调用栈中,所以被称为stack Reconciler
。React16的Reconciler
基于Fiber节点实现,被称为Fiber Reconciler
。 - 作为静态的数据结构来说,每个Fiber节点对应一个
React element
,保存了该组件的类型(函数组件/类组件/原生组件…)、对应的DOM节点等信息。 - 作为动态的工作单元来说,每个Fiber节点保存了本次更新中该组件改变的状态、要执行的工作(需要被删除/被插入页面中/被更新…)
接来下从源码角度来解析Fiber的结构,我阅读的源码是react 17.0.2
版本,以old
文件为主,点击这里查看源码,从上面所提到的三层含义来分析,简单标识一下:
f
unction FiberNode(
tag: WorkTag,
pendingProps: mixed,
key: null | string,
mode: TypeOfMode,
) {
// 作为静态数据结构的属性 start
// Fiber对应组件的类型 Function/Class/Host...
this.tag = tag;
// key属性
this.key = key;
// 大部分情况同type,某些情况不同,比如FunctionComponent使用React.memo包裹
this.elementType = null;
// 对于 FunctionComponent,指函数本身,对于ClassComponent,指class,对于HostComponent,指DOM节点tagName
this.type = null;
// Fiber对应的真实DOM节点
this.stateNode = null;
// 作为静态数据结构的属性 end
// 作为架构,用于连接其他Fiber节点形成Fiber树 start
this.return = null; // 指向父Fiber
this.child = null; // 指向第一个子Fiber
this.sibling = null;// 指向第一个兄弟Fiber
this.index = 0;
// 作为架构,用于连接其他Fiber节点形成Fiber树 end
this.ref = null;
// 作为动态的工作单元的属性 start
this.pendingProps = pendingProps;
this.memoizedProps = null;
this.updateQueue = null;
this.memoizedState = null;
this.dependencies = null;
// 作为动态的工作单元的属性 end
this.mode = mode;
this.effectTag = NoEffect;
this.nextEffect = null;
this.firstEffect = null;
this.lastEffect = null;
// 调度优先级相关
this.lanes = NoLanes;
this.childLanes = NoLanes;
// 指向该fiber在另一次更新时对应的fiber
this.alternate = null;
}