fiber体系介绍
React中的Virtual DOM体系,叫做fiber体系。 fiber是一个Virtual DOM节点,存储了当前节点的信息(自身的信息,父级节点指针,子节点指针,兄弟节点指针等等)。
fiber的类型
如下图,fiber有很多种类型,在执行更新的时候,只有组件类型的fiber会通过执行渲染函数产生新的React Element。
export const FunctionComponent = 0; // 对应函数组件
export const ClassComponent = 1; // 对应类组件
export const IndeterminateComponent = 2; // 初始化的时候不知道是函数组件还是类组件
export const HostRoot = 3; // RootFiber可以理解为根节点,通过reactDom.render()产生的根元素
export const HostPortal = 4;
export const HostComponent = 5;// DOM元素比如<div>
export const HostText = 6;// 文本节点
export const Fragment = 7;// 对应<React.Fragment>
export const Mode = 8;// 对应<React.StrictMode>
export const ContextConsumer = 9;// 对应<Context.Consumer>
export const ContextProvider = 10;// 对应<Context.Provider>
export const ForwardRef = 11;// 对应<React.forwardRef>
export const Profiler = 12;// 对应<React.Profiler/>
export const SuspenseComponent = 13;// 对应<Suspense>
export const MemoComponent = 14;// 对应<React.memo>返回的组件
创建fiberNode的方法
export function FiberNode (tag, pendingProps, key, mode) {
this.tag = tag;// 说明是什么类型的fiber
this.key = key;// key调和子节点的时候用到
this.type = null;// 如果是DOM元素,指的是元素类型,如div, 如果是组件,指的是组件对应的类或者函数
this.stateNode = null;// 指向对应的真实元素,类组件指向组件实例
this.return = null;//指向父级fiber
this.child = null;// 指向子级fiber
this.sibling = null;// 指向兄弟fiber
this.index = 0;// 索引
this.ref = null;//ref指向,函数或者ref对象
this.pendingProps = pendingProps;// 在一次更新中,代表重新创建Element生成的props
this.memoizedProps = null;// 记录上一次更新完毕后的props
this.updateQueue = null;// 记录更新队列
this.memoizedState = null;// 类组件保存State信息,函数组件保存Hooks信息,DOM元素为null
this.dependencies = null;// Context依赖项
this.mode = mode;// 描述fiber树的模式,比如concurrentMode模式
// V16老版本
this.effectTag = null;// effect标签,用于收集effectList
this.nextEffect = null;// 指向下一个effect
this.firstEffect = null;// 第一个Effect
this.lastEffect = null;// 最后一个effect
this.expirationTime = NoWork;// 过期时间,判断任务是否过期,在ReactV17 及以上版本用lane表示
// V17版本
this.flags = NoFlags;// 类似于effectTag
this.subtreeFlags = NoFlags;// 当前fiber的子代fiber是否有flags
this.lanes = NoLanes; // 是否有更新标志
this.childLanes = NoLanes; // Children是否有更新标志
//
this.alternate = null;// 双缓存树,指向缓存的fiber。更新阶段,两个树互相交替
}
Element、fiber、DOM三者之间的关系
fiber体系出现的背景及目的
在React V15以及之前的版本,React对于 Virtual DOM是采用递归的方式进行遍历更新的。
一次更新,就会从应用根部递归更新,递归一旦开始,中途无法中断,随着项目越来越复杂,层级越来越深,导致更新的时间越来越长,给前端交互上的体验就是卡顿。
React V16为了解决卡顿问题,引入了fiber体系。
fiber树的构建
双缓存树的设计
在内存中构建并替换的技术叫做双缓存。比如canvas在绘制动画的时候,如果上一帧计算量比较大,导致清除上一帧的画面到渲染出最新画面有比较长的时间间隔,就会出现白屏。如果提前在内存中绘制好,直接替换渲染好的帧,就能给用户更好的体验。
React用workInProgress(内存中的树)和current(正在视图中渲染的树)来实现更新逻辑。
React的更新流程
- 在更新对应fiber的lane,向上回溯更新父级链上的childLanes
- 从根节点开始,一直向下查找定位到发生变化的Fiber节点
- 重新执行组件得到新的Element,对比oldProps和newProps,对比lane和renderLane判断是否要更新,是否是需要更新的节点
- 对新生成的Element和对应的oldFiber进行diff,并创建新的Fiber(可能会复用oldFiber),变动的类型用flag字段做标记,更新标志如Placement(插入元素)、Update(更新元素)
- 更新DOM
- 浏览器绘制
Element和fiber的区别: Element是用数组来存储子元素的,而fiber是用child + sibling的对象结构存储的。