React源码浅见1 fiber的了解

伴随着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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值