• 首页 首页 icon
  • 工具库 工具库 icon
    • IP查询 IP查询 icon
  • 内容库 内容库 icon
    • 快讯库 快讯库 icon
    • 精品库 精品库 icon
    • 问答库 问答库 icon
  • 更多 更多 icon
    • 服务条款 服务条款 icon

React Fiber 初始化

武飞扬头像
会飞的鱼571
帮助3

由来

Fiber Reconciler 与 stack Reconciler

reactv15 版本之前推崇的是 stack Reconciler 协调算法, 而react16版本以后是对stack Reconciler进行了重写, stack Reconciler的实现使用了同步递归模型, 该模型依赖于内置堆栈遍历


如果一直使用内置调用堆栈, 它将一直工作, 直到堆栈为空, 如果我们可以随意中断调用堆栈并手动操作堆栈帧, 这就有了react Filber, Fiber是内置堆栈的重新实现, 用于react , 可以看作是一个虚拟堆栈帧

由于内置stack reconciler的局限性, 使得DOM的更新过程是同步的,就是说, 在虚拟DOM的比对过程中,如果发现一个元素实例有更新,则会立即执行同步操作,提交到真实DOM的更改 , 想象这在用户操作, 动画,布局以及手势 体验会很差. 为解决这个问题, react团队实现了虚拟堆栈帧也就是建立了多个包含节点和指针的链表数据结构, 每一个fiber就是一个单元,这个对象存储了一定的组件相关的数据信息, 而指针的指向 是串联整个Fiber树, 重新定义虚拟堆栈帧带来的优点是, 可以将堆栈保留在内存中,在需要执行的时候执行他们, 使得暂停遍历停止堆栈递归成为可能.

基础概念

在了解Fiber架构, 先了解几个概念

work

在react Reconciliation 过程中出现的各种必须计算的活动, 比如state update , props update 或 refs update, 这些活动统一称为work .

Fiber对象

文件位置:packages/react-reconciler/src/ReactFiber.js

每一个React元素对应一个Fiber对象,一个Fiber对象通常代表work的一个单元. Fiber对象有几个属性,这些属性指向其他Fiber对象

  • child:对应父fiber节点的子fiber
  • sibling:对应fiber节点的同类兄弟节点
  • return:对应子fiber节点的父节点

因此fiber可以理解为是一个包含React元素上下文信息的数据域节点,以及child,sibling和return等指针域构成的链表结构. fiber 对象的属性如下:

Fiber = {
    // 标识 fiber 类型的标签,详情参看下述 WorkTag
    tag: WorkTag,

    // 指向父节点
    return: Fiber | null,

    // 指向子节点
    child: Fiber | null,

    // 指向兄弟节点
    sibling: Fiber | null,

    // 在开始执行时设置 props 值
    pendingProps: any,

    // 在结束时设置的 props 值
    memoizedProps: any,

    // 当前 state
    memoizedState: any,

    // Effect 类型,详情查看以下 effectTag
    effectTag: SideEffectTag,

    // effect 节点指针,指向下一个 effect
    nextEffect: Fiber | null,

    // effect list 是单向链表,第一个 effect
    firstEffect: Fiber | null,

    // effect list 是单向链表,最后一个 effect
    lastEffect: Fiber | null,

    // work 的过期时间,可用于标识一个 work 优先级顺序
    expirationTime: ExpirationTime,
};

从react元素创建一个fiber对象 文件位置:react-reconciler/src/ReactFiber.js

    export function createFiberFromElement(
    element: ReactElement,
    mode: TypeOfMode,
    expirationTime: ExpirationTime
    ): Fiber {
    const fiber = createFiberFromTypeAndProps(type, key, pendingProps, owner, mode,   expirationTime);
    return fiber;
    }

workTag

上述fiber对象的tag属性值,称作workTag,用于标识一个React元素的类型,如下所示:

export const FunctionComponent = 0;
export const ClassComponent = 1;
export const IndeterminateComponent = 2; // Before we know whether it is function or class
export const HostRoot = 3; // Root of a host tree. Could be nested inside another node.
export const HostPortal = 4; // A subtree. Could be an entry point to a different renderer.
export const HostComponent = 5;
export const HostText = 6;
export const Fragment = 7;
export const Mode = 8;
export const ContextConsumer = 9;
export const ContextProvider = 10;
export const ForwardRef = 11;
export const Profiler = 12;
export const SuspenseComponent = 13;
export const MemoComponent = 14;
export const SimpleMemoComponent = 15;
export const LazyComponent = 16;
export const IncompleteClassComponent = 17;
export const DehydratedSuspenseComponent = 18;
export const EventComponent = 19;
export const EventTarget = 20;
export const SuspenseListComponent = 21;

EffectTag

文件位置:shared/ReactSideEffectTags.js

上述fiber对象的effectTag属性值,每一个fiber节点都有一个相关联的effectTag值,我们把不能在 render 阶段完成的一些 work 称之为副作用,React 罗列了可能存在的各类副作用,如下所示:

export const NoEffect = /*              */ 0b000000000000;
export const PerformedWork = /*         */ 0b000000000001;

export const Placement = /*             */ 0b000000000010;
export const Update = /*                */ 0b000000000100;
export const PlacementAndUpdate = /*    */ 0b000000000110;
export const Deletion = /*              */ 0b000000001000;
export const ContentReset = /*          */ 0b000000010000;
export const Callback = /*              */ 0b000000100000;
export const DidCapture = /*            */ 0b000001000000;
export const Ref = /*                   */ 0b000010000000;
export const Snapshot = /*              */ 0b000100000000;
export const Passive = /*               */ 0b001000000000;

export const LifecycleEffectMask = /*   */ 0b001110100100;
export const HostEffectMask = /*        */ 0b001111111111;

export const Incomplete = /*            */ 0b010000000000;
export const ShouldCapture = /*         */ 0b100000000000;

Reconciliation 和 Scheduling

协调(Reconciliation): 根据diff算法来对比虚拟DOM,从而确认哪些部分的react元素需要更改 调度(Scheduling): 可以理解在确定什么时候执行work的过程

Render阶段和commit阶段

React 的生命周期主要分为两个阶段:render 阶段和 commit 阶段。其中 commit 阶段又可以细分为 pre-commit 阶段和 commit 阶段,如下图所示

学新通

在render阶段,react可以根据当前可用的时间片处理一个或多个fiber节点,并且得益于fiber对象存储的元素上下文信息以及指针域构成的链表结构,使其能够执行到一半的工作保存在有内存的链表中. 当react停止并完成保存的工作后,让出时间去处理一些其他优先级更高的事情. 之后,在重新获取获取到可用的时间片后, 它能够根据之前保存在内存的上下文信息通过快速遍历的方式找到停止的fiber节点并继续工作.由于在此阶段执行的工作并不会导致任何用户可见的更改,因为并没有提交到真实的DOM.所以,我们说是fiber能让调度实现暂停、中止、以及重新开始增量渲染的能力.相反,在commit阶段,work执行总是同步的,这是因为在此阶段执行的工作将导致用户可见的更改.这就是为什么在commit阶段,react需要一次提交并完成这些工作的原因

current树和workInProgress树

首次渲染后,react会生成一个对应于ui渲染的fiber树,称之为current树,实际上,react在调用生命周期钩子函数时就是通过判断是否存在current来区分何时执行componentDidMount和componentDidUpdate.当 React 遍历 current 树时,它会为每一个存在的 fiber 节点创建了一个替代节点,这些节点构成一个 workInProgress 树。后续所有发生 work 的地方都是在 workInProgress 树中执行,如果该树还未创建,则会创建一个 current 树的副本,作为 workInProgress 树。当 workInProgress 树被提交后将会在 commit 阶段的某一子阶段被替换成为 current 树。

这里增加两个树的主要原因是为了避免更新的丢失。比如,如果我们只增加更新到 workInProgress 树,当 workInProgress 树通过从 current 树中克隆而重新开始时,一些更新可能会丢失。同样的,如果我们只增加更新到 current 树,当 workInProgress 树被提交后会被替换为 current 树,更新也会被丢失。通过在两个队列都保持更新,可以确保更新始终是下一个 workInProgress 树的一部分。并且,因为 workInProgress 树被提交成为 current 树,并不会出现相同的更新而被重复应用两次的情况。

Effects list

effect list 可以理解为是一个存储 effectTag 副作用列表容器。它是由 fiber 节点和指针 nextEffect 构成的单链表结构,这其中还包括第一个节点 firstEffect,和最后一个节点 lastEffect。如下图所示

学新通 React 采用深度优先搜索算法,在 render 阶段遍历 fiber 树时,把每一个有副作用的 fiber 筛选出来,最后构建生成一个只带副作用的 effect list 链表。
在 commit 阶段,React 拿到 effect list 数据后,通过遍历 effect list,并根据每一个 effect 节点的 effectTag 类型,从而对相应的 DOM 树执行更改。

这篇好文章是转载于:学新通技术网

  • 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
  • 本站站名: 学新通技术网
  • 本文地址: /boutique/detail/tanhfhchfe
系列文章
更多 icon
同类精品
更多 icon
继续加载