我的编程空间,编程开发者的网络收藏夹
学习永远不晚

ReactFiber构建源码解析

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

ReactFiber构建源码解析

引言

前面的章节,我们在render方法里,简单涉及到了fiber,那么:

  • fiber究竟是什么?
  • fiber的出现解决了什么问题?
  • 为什么fiber之前的版本,没办法解决

下面,我们带着这些问题,来仔细聊聊Fiber

一. Fiber是什么

在fiber出现之前,react的架构体系只有协调器reconciler和渲染器render。

当前有新的update时,react会递归所有的vdom节点,如果dom节点过多,会导致其他事件影响滞后,造成卡顿。即之前的react版本无法中断工作过程,一旦递归开始无法停留下来。

为了解决这一系列问题,react历时多年重构了底层架构,引入了fiber。 fiber的出现使得react能够异步可中断工作任务,并且可以在浏览器空闲时,从中断处继续往下工作。 当出现多个高优先任务并行时,react引入lane模型取代之前的expireTime机制。

这里提及下vue工作原理,为什么有新的update任务时,vue不需要做全量递归,而react需要?(留个悬念,大家可以先思考下)

fiber本质上是一种数据结构,在react17后,没有vdom概念,一切皆是Fiber,但Fiber != vdom。

二. FiberRoot

FiberRoot是react启动阶段,要构建的fiber对象。与之容易混淆是rootFiber,下面会具体介绍。

fiberRoot生成

  function createFiberRoot(containerInfo, tag, hydrate, hydrationCallbacks) {
    var root = new FiberRootNode(containerInfo, tag, hydrate);
    // stateNode is any.
    var uninitializedFiber = createHostRootFiber(tag);
    root.current = uninitializedFiber;
    uninitializedFiber.stateNode = root;
    initializeUpdateQueue(uninitializedFiber);
    return root;
  }

fiberRoot类

function FiberRootNode(containerInfo, tag, hydrate) {
    this.tag = tag;
    this.containerInfo = containerInfo;
    this.pendingChildren = null;
    this.current = null;
    this.pingCache = null;
    this.finishedWork = null;
    this.timeoutHandle = noTimeout;
    this.context = null;
    this.pendingContext = null;
    this.hydrate = hydrate;
    this.callbackNode = null;
    this.callbackPriority = NoLanePriority;
    this.eventTimes = createLaneMap(NoLanes);
    this.expirationTimes = createLaneMap(NoTimestamp);
    this.pendingLanes = NoLanes;
    this.suspendedLanes = NoLanes;
    this.pingedLanes = NoLanes;
    this.expiredLanes = NoLanes;
    this.mutableReadLanes = NoLanes;
    this.finishedLanes = NoLanes;
    this.entangledLanes = NoLanes;
    this.entanglements = createLaneMap(NoLanes);
    {
      this.mutableSourceEagerHydrationData = null;
    }
    {
      this.interactionThreadID = unstable_getThreadID();
      this.memoizedInteractions = new Set();
      this.pendingInteractionMap = new Map();
    }
    {
      switch (tag) {
        case BlockingRoot:
          this._debugRootType = 'createBlockingRoot()';
          break;
        case ConcurrentRoot:
          this._debugRootType = 'createRoot()';
          break;
        case LegacyRoot:
          this._debugRootType = 'createLegacyRoot()';
          break;
      }
    }
  }

fiberRoot本质上fiber的顶层对象,其中tag记录了几种启动模式:

  • 0普通模式
  • 1 小部分并发模式
  • 2 并发模式

启动模式的不同,在后协调阶段有具体差异。

该类引用的实例,即current对象是rootFiber。finishedWork是fiber完成协调器work之后的结果,下面有许多字段都带有lane,这里可以先不关注,后面章节我们单独聊聊Lane模型

三. RootFiber

rootFiber生成

  function createFiberRoot(containerInfo, tag, hydrate, hydrationCallbacks) {
    var root = new FiberRootNode(containerInfo, tag, hydrate);
    // stateNode is any.
    var uninitializedFiber = createHostRootFiber(tag);
    root.current = uninitializedFiber;
    uninitializedFiber.stateNode = root;
    initializeUpdateQueue(uninitializedFiber);
    return root;
  }

createHostRootFiber

  function createHostRootFiber(tag) {
    var mode;
    if (tag === ConcurrentRoot) {
      mode = ConcurrentMode | BlockingMode | StrictMode;
    } else if (tag === BlockingRoot) {
      mode = BlockingMode | StrictMode;
    } else {
      mode = NoMode;
    }
    if ( isDevToolsPresent) {
      // Always collect profile timings when DevTools are present.
      // This enables DevTools to start capturing timing at any point–
      // Without some nodes in the tree having empty base times.
      mode |= ProfileMode;
    }
    return createFiber(HostRoot, null, null, mode);
  }

FiberNode

function FiberNode(tag, pendingProps, key, mode) {
    this.tag = tag;
    this.key = key;
    this.elementType = null;
    this.type = null;
    this.stateNode = null;
    this.return = null;
    this.child = null;
    this.sibling = null;
    this.index = 0;
    this.ref = null;
    this.pendingProps = pendingProps; 
    this.memoizedProps = null; 
    this.updateQueue = null; 
    this.memoizedState = null;  
    this.dependencies = null;
    this.mode = mode; // Effects
    this.flags = NoFlags;     
    this.nextEffect = null;  
    this.firstEffect = null; 
    this.lastEffect = null; 
    this.lanes = NoLanes;
    this.childLanes = NoLanes;
    this.alternate = null;
    {
      this.actualDuration = Number.NaN;
      this.actualStartTime = Number.NaN;
      this.selfBaseDuration = Number.NaN;
      this.treeBaseDuration = Number.NaN; // It's okay to replace the initial doubles with smis after initialization.
      this.actualDuration = 0;
      this.actualStartTime = -1;
      this.selfBaseDuration = 0;
      this.treeBaseDuration = 0;
    }
    {
      // This isn't directly used but is handy for debugging internals:
      this._debugID = debugCounter++;
      this._debugSource = null;
      this._debugOwner = null;
      this._debugNeedsRemount = false;
      this._debugHookTypes = null;
      if (!hasBadMapPolyfill && typeof Object.preventExtensions === 'function') {
        Object.preventExtensions(this);
      }
    }
  }

到这里,fiberNode是一个类,往下的所有dom都将实例化这个类。

这里的tag依旧是启动模式,return是父节点fiber,child是子节点第一个fiber,sibling是兄弟节点的fiber。

另外,flags很重要,在后续work阶段会大量使用,另外flags和lane都是二进制数据对象,后面大量运用位运算。

effect对象,会在work loop阶段生成,也就是副作用,比如我们写的useEffect,都在work lopp阶段被挂载。

每个fiber的stateNode指向具体实例节点。

flags

export type Flags = number;
export const NoFlags =  0b000000000000000000;
export const PerformedWork =  0b000000000000000001;
export const Placement =  0b000000000000000010;
export const Update =  0b000000000000000100;
export const PlacementAndUpdate =  0b000000000000000110;
export const Deletion =  0b000000000000001000;
export const ContentReset =  0b000000000000010000;
export const Callback =  0b000000000000100000;
export const DidCapture =  0b000000000001000000;
export const Ref =  0b000000000010000000;
export const Snapshot =  0b000000000100000000;
export const Passive =  0b000000001000000000;
// TODO (effects) Remove this bit once the new reconciler is synced to the old.
export const PassiveUnmountPendingDev =  0b000010000000000000;
export const Hydrating =  0b000000010000000000;
export const HydratingAndUpdate =  0b000000010000000100;
// Passive & Update & Callback & Ref & Snapshot
export const LifecycleEffectMask =  0b000000001110100100;
// Union of all host effects
export const HostEffectMask =  0b000000011111111111;
// These are not really side effects, but we still reuse this field.
export const Incomplete =  0b000000100000000000;
export const ShouldCapture =  0b000001000000000000;
export const ForceUpdateForLegacySuspense =  0b000100000000000000;
export const PassiveStatic =  0b001000000000000000;
export const BeforeMutationMask =  0b000000001100001010;
export const MutationMask =  0b000000010010011110;
export const LayoutMask =  0b000000000010100100;
export const PassiveMask =  0b000000001000001000;
export const StaticMask =  0b001000000000000000;
export const MountLayoutDev =  0b010000000000000000;
export const MountPassiveDev =  0b100000000000000000;

lane

export const NoLanes: Lanes =  0b0000000000000000000000000000000;
export const NoLane: Lane =  0b0000000000000000000000000000000;
export const SyncLane: Lane =  0b0000000000000000000000000000001;
export const SyncBatchedLane: Lane =  0b0000000000000000000000000000010;
export const InputDiscreteHydrationLane: Lane =  0b0000000000000000000000000000100;
const InputDiscreteLanes: Lanes =  0b0000000000000000000000000011000;
const InputContinuousHydrationLane: Lane =  0b0000000000000000000000000100000;
const InputContinuousLanes: Lanes =  0b0000000000000000000000011000000;
export const DefaultHydrationLane: Lane =  0b0000000000000000000000100000000;
export const DefaultLanes: Lanes =  0b0000000000000000000111000000000;
const TransitionHydrationLane: Lane =  0b0000000000000000001000000000000;
const TransitionLanes: Lanes =  0b0000000001111111110000000000000;
const RetryLanes: Lanes =  0b0000011110000000000000000000000;
export const SomeRetryLane: Lanes =  0b0000010000000000000000000000000;
export const SelectiveHydrationLane: Lane =  0b0000100000000000000000000000000;
const NonIdleLanes =  0b0000111111111111111111111111111;
export const IdleHydrationLane: Lane =  0b0001000000000000000000000000000;
const IdleLanes: Lanes =  0b0110000000000000000000000000000;
export const OffscreenLane: Lane =  0b1000000000000000000000000000000;

关于flags和lane,我们先有个感性认知,后面章节单独分析

initializeUpdateQueue

  function initializeUpdateQueue(fiber) {
    var queue = {
      baseState: fiber.memoizedState,
      firstBaseUpdate: null,
      lastBaseUpdate: null,
      shared: {
        pending: null
      },
      effects: null
    };
    fiber.updateQueue = queue;
  }

rootFiber上调用initializeUpdateQueue,初始化queue对象,这里仅仅是初始化对象而已,并不是许多文章说fiber加入更新队列。fiber的更新队列和这里没有任何关系,fiber的更新队列是后续schedule调度的task queue。

四. Root

root对象是legacyCreateRootFromDOMContainer方法的返回对象,这个对象是全局唯一,并贯穿了react后续的各阶段计算。

至此,我们对应fiber有个感性的认知。另外需要说明的是,每个dom节点都是fiber,fiber通过return, child, sibling关联其他fiber,本质上fiber是个链表数据结构,这一点和后续的effect数据结构还是有区别的。

在root生成后,首次初始化应用,将进入核心updateContainer方法

updateContainer(children, fiberRoot, parentComponent, callback);

updateContainer

  function updateContainer(element, container, parentComponent, callback) {
      // ...省略eventTime和lane相关,后续单独介绍
    var update = createUpdate(eventTime, lane); // Caution: React DevTools currently depends on this property
    // being called "element".
    update.payload = {
      element: element
    };
    callback = callback === undefined ? null : callback;
    if (callback !== null) {
      {
        if (typeof callback !== 'function') {
          error('render(...): Expected the last optional `callback` argument to be a ' + 'function. Instead received: %s.', callback);
        }
      }
      update.callback = callback;
    }
    enqueueUpdate(current$1, update);
    scheduleUpdateOnFiber(current$1, lane, eventTime);
    return lane;
  }

createUpdate

  function createUpdate(eventTime, lane) {
    var update = {
      eventTime: eventTime,
      lane: lane,
      tag: UpdateState,
      payload: null,
      callback: null,
      next: null
    };
    return update;
  }
  function enqueueUpdate(fiber, update) {
    var updateQueue = fiber.updateQueue;
    if (updateQueue === null) {
      // Only occurs if the fiber has been unmounted.
      return;
    }
    var sharedQueue = updateQueue.shared;
    var pending = sharedQueue.pending;
    if (pending === null) {
      // This is the first update. Create a circular list.
      update.next = update;
    } else {
      update.next = pending.next;
      pending.next = update;
    }
    sharedQueue.pending = update;
  }

在rootFiber上,创建更新对象,并挂载至enqueueUpdate上。

update对象上payload很重要,后面在协调阶段-fiber树构建阶段是重要的输入。

update对象也是链表结构,通过next关联下一个update对象。

至此,fiber的初始化对象到这里就结束了,目前内存中只存在fiberRoot和rootFiber对象,下面调的scheduleUpdateOnFiber方法,将正式进入协调阶段,更多关于React构建Fiber的资料请关注编程网其它相关文章!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

ReactFiber构建源码解析

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

ReactFiber构建源码解析

这篇文章主要为大家介绍了ReactFiber构建源码解析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
2023-02-06

ReactFiber构建beginWork源码解析

这篇文章主要为大家介绍了ReactFiber构建beginWork源码解析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
2023-02-06

ReactFiber源码深入分析

Fiber可以理解为一个执行单元,每次执行完一个执行单元,ReactFiber就会检查还剩多少时间,如果没有时间则将控制权让出去,然后由浏览器执行渲染操作,这篇文章主要介绍了ReactFiber架构原理剖析,需要的朋友可以参考下
2022-11-13

React Fiber构建completeWork源码解析

这篇文章主要为大家介绍了React Fiber构建completeWork源码解析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
2023-02-06

ReactFiber树的构建和替换过程讲解

ReactFiber树的创建和替换过程运用了双缓存技术,直接将旧的fiber树替换成新的fiber树,这样做的好处是省去了直接在页面上渲染时的计算时间,避免计算量大导致的白屏、卡顿,现在你一定还不太理解,下面进行详细讲解,需要的朋友可以参考下
2022-12-15

TensorFlow源代码构建流程记录解析

这篇文章主要为大家介绍了TensorFlow源代码构建流程记录解析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
2023-01-03

go语言构建顺序源码分析

这篇文章主要介绍“go语言构建顺序源码分析”,在日常操作中,相信很多人在go语言构建顺序源码分析问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”go语言构建顺序源码分析”的疑惑有所帮助!接下来,请跟着小编一起来
2023-07-05

ConcurrentHashMap 存储结构源码解析

这篇文章主要为大家介绍了ConcurrentHashMap 存储结构源码解析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
2022-11-13

从源码构建docker-ce的过程分析

这篇文章主要介绍了从源码构建docker-ce的过程,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
2022-12-20

微前端架构ModuleFederationPlugin源码解析

这篇文章主要为大家介绍了微前端架构ModuleFederationPlugin源码解析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
2022-11-13

python架构PyNeuraLogic源码分析

这篇“python架构PyNeuraLogic源码分析”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“python架构PyNe
2023-07-05

kubeadm init快速搭建k8s源码解析

这篇文章主要为大家介绍了kubeadm init快速搭建k8s源码解析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
2023-05-14

vue3模块创建runtime-dom源码解析

这篇文章主要为大家介绍了vue3模块创建runtime-dom源码解析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
2023-01-12

SpringApplicationListener源码解析

这篇文章主要为大家介绍了SpringApplicationListener源码解析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
2023-01-15

TC集群Seata1.6高可用架构源码解析

这篇文章主要为大家介绍了TC集群Seata1.6高可用架构源码解析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
2022-12-26

编程热搜

目录