前言
上一篇简单介绍了一下ReactDom创建根节点的逻辑,我看看到一个类型:FiberRoot,要讲FiberRoot也要先说一下Fiber了,所以这里我们先来简单介绍一下Fiber,它的属性很多,我觉得我们目前只需要大概了解一下即可,后续有遇到的地方再详细了解。
为什么需要Fiber
我们一起来回忆一下我们之前说的ReactElement数据结构,它的结构很简单
export type ReactElement = {
$$typeof: any,
type: any,
key: any,
ref: any,
props: any,
};
除了有个$$typeof标识之外,其他的type,key,ref,props基本只对jsx编译后的结果做了简单的转换(提取出了key,ref,其他属性放入props)
那么它有什么问题呢?
- 无法表示它与各个节点之间的关系(父亲,兄弟节点)
- 字段过于简单,无法存储状态等信息,无法作为一个独立的工作单元
所以,我们需要一种新的数据结构,它就需要弥补ReactElement的缺点,它就是Fiber,它也是虚拟DOM在react中的实现
Fiber的数据结构
那么Fiber的数据结构是什么样的呢?
export type Fiber = {
// Fiber标识,number类型
// 比如0表示FunctionComponent,3表示HostRoot
tag: WorkTag,
// 元素key,也就是ReactElement上的key
key: null | string,
// 元素类型,也就是ReactElement上的type,比如"div"
elementType: any,
// Fiber真实类型,和elementType基本一样
type: any,
// 实例对象,比如说类组件的实例,RootFiber的FiberRoot
stateNode: any,
// 指向父节点Fiber
return: Fiber | null,
// 指向孩子节点Fiber
child: Fiber | null,
// 指向兄弟节点Fiber
sibling: Fiber | null,
// 同层级Fiber的索引,没有兄弟节点的话是0
// index,key要一起用于diff
index: number,
// ReactElement的ref,也就是我们开发中使用的ref
ref:
| null
| (((handle: mixed) => void) & {_stringRef: ?string, ...})
| RefObject,
refCleanup: null | (() => void),
// 新Props
pendingProps: any,
// 旧Props
memoizedProps: any,
// 状态更新队列,setState就存在这上面
updateQueue: mixed,
// 旧State
memoizedState: any,
// contexts, events依赖
dependencies: Dependencies | null,
// 渲染模式,2进制表示
// 比如0b0000000表示NoMode,0b0000001表示ConcurrentMode
mode: TypeOfMode,
// 副作用标记,二进制表示
// 0b0000000000000000000000000000表示NoFlags(不更新,diff时跳过)
// 0b0000000000000000000000000010表示Placement(插入)
// 0b0000000000000000000000000100表示Update(插入更新)
flags: Flags,
// 子树副作用标识
subtreeFlags: Flags,
// 子节点中需要删除的节点
deletions: Array<Fiber> | null,
// 待完善
lanes: Lanes,
childLanes: Lanes,
/**
* 双缓冲:防止数据丢失,提高效率(之后Dom-diff的时候可以直接比较或者使用
* React在进行diff更新时,会维护两颗fiber树,一个是当前正在展示的,一个是
* 通过diff对比后要更新的树,这两棵树中的每个fiber节点通过 alternate 属性
* 进行互相指向。
*/
alternate: Fiber | null,
actualDuration?: number,
actualStartTime?: number,
selfBaseDuration?: number,
treeBaseDuration?: number,
};
FiberRoot
我这里就列出一些基础的属性,其他的我们后续如果用到再补充吧
type BaseFiberRootProperties = {
// 标记类型: 0|1分别对应 LegacyRoot|ConcurrentRoot
tag: RootTag,
// 根节点元素,createRoot接受的参数
containerInfo: Container,
// 指向RootFiber,也就是根节点的Fiber
current: Fiber,
// 已经完成的标记的 FiberRoot对象, commit的对象
finishedWork: Fiber | null,
// 待处理的lane集合
pendingLanes: Lanes;
// 挂起的lane集合
suspendedLanes: Lanes;
// 已经标记过的的lane集合
pingedLanes: Lanes;
// 待清除的effect
pendingPassiveEffects: PendingPassiveEffects
// ...
};
FiberRoot,RootFiber的关系?
听起来有点拗口,但是其实抓住问题的本质就行:
FiberRoot本质是root,也就是所有Fiber的根节点,它的数据结构和Fiber不同
RootFiber本质是fiber,指的是根部的Fiber节点,它就是一个Fiber
FiberRoot和RootFiber的指向关系如图:
Fiber的遍历流程
通过上面的数据结构我们可以知道,Fiber是一个链表结构,可以组成一个树,其遍历的指针就在 child, sibling, return 三个属性上,child指向子Fiber,sibling指向兄弟Fiber,return指向父亲Fiber,它的遍历过程其实就是一个深度优先的遍历(DFS)
- 如果有child节点就优先遍历child节点,直到child节点为null为止
- 如果child节点为null就遍历sibling节点,如果sibling节点有child节点就继续遍历child
- sibling节点的child遍历完之后就遍历sibling 的sibling
- sibling遍历完之后就遍历return节点
- 直到回到根节点