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

ReactFiber构建beginWork源码解析

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

北京

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

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

看不清楚,换张图片

免费获取短信验证码

ReactFiber构建beginWork源码解析

引言

前文我们介绍了fiber的基本概念,以及fiber在初始化阶段生成了fiberRoot和rootFiber 2个对象。

但,整个fiber树还没有构建,未进入reconciler阶段。

本篇,我们将介绍,fiber链表的构建之-beginWork阶段

一. scheduleUpdateOnFiber

function scheduleUpdateOnFiber(fiber, lane, eventTime) {
  // ...
  if (root === workInProgressRoot) {
      {
        workInProgressRootUpdatedLanes = mergeLanes(workInProgressRootUpdatedLanes, lane);
      }
    }
  // ...
  if (lane === SyncLane) {
      if ( // Check if we're inside unbatchedUpdates
      (executionContext & LegacyUnbatchedContext) !== NoContext && // Check if we're not already rendering
      (executionContext & (RenderContext | CommitContext)) === NoContext) {
        // ...
        performSyncWorkOnRoot(root)
      }else {
        // ...
        ensureRootIsScheduled(root, eventTime);
      }
  }else {
    // ...
     ensureRootIsScheduled(root, eventTime);
     schedulePendingInteractions(root, lane);
  }
}

fiber在内存中,会有两份数据,一个是当前的,一个是在内存中正在构建的。

这里 根据不同的启动模式,进行下面的协调阶段。在17版本中,一般使用sync模式。18版本默认开启并发模式。

二. performSyncWorkOnRoot

同步模式下的流程,如果是并发模式,会进入schedule异步调度,最终还会执行performSyncWorkOnRoot。

function performSyncWorkOnRoot(root) {
    flushPassiveEffects();
    var lanes;
    var exitStatus;
    if (root === workInProgressRoot && includesSomeLane(root.expiredLanes, workInProgressRootRenderLanes)) {
      lanes = workInProgressRootRenderLanes;
      exitStatus = renderRootSync(root, lanes);
      if (includesSomeLane(workInProgressRootIncludedLanes, workInProgressRootUpdatedLanes)) {
        lanes = getNextLanes(root, lanes);
        exitStatus = renderRootSync(root, lanes);
      }
    } else {
      lanes = getNextLanes(root, NoLanes);
      exitStatus = renderRootSync(root, lanes);
    }
    // ...
    var finishedWork = root.current.alternate;
    root.finishedWork = finishedWork;
    root.finishedLanes = lanes;
    commitRoot(root); 
    ensureRootIsScheduled(root, now());
    return null;
}

performSyncWorkOnRoot是reconciler阶段所有的执行入口,首次渲染将进入renderRootSync。

问题来了,为什么要先执行flushPassiveEffects?这里留个悬念,在后续的更新流程中我们再提及。

关于nextLanes,这里我先抛开,先理解为render优先级,lane模型会在后续章节系统性的讲解。

renderRootSync

function renderRootSync(root, lanes) {
  if (workInProgressRoot !== root || workInProgressRootRenderLanes !== lanes) {
      prepareFreshStack(root, lanes); 
      startWorkOnPendingInteractions(root, lanes);
    }
   // ...
    do {
      try {
        workLoopSync();
        break;
      } catch (thrownValue) {
        handleError(root, thrownValue);
      }
    } while (true);
    // ...
}

prepare阶段,可以构建双缓存fiber,即workInProgressRoot,内存中的fiber通过之前的createFiber方法调用,当前fiber和内存中fiber通过alternate相互引用。

workLoopSync即react两大工作循环中的第一层循环,从这里开始构建fiber链表。

workLoopSync

function workLoopSync() {
    while (workInProgress !== null) {
      performUnitOfWork(workInProgress);
    }
  }

这里是同步构建的情况,值得对比的是另外一个方法:

function workLoopConcurrent() {
    while (workInProgress !== null && !shouldYield()) {
      performUnitOfWork(workInProgress);
    }
  }

此方法是并发模式下的工作模式,两者区别在于shouldYield。shouldYield由schedule调度器控制,react自己实现了一套浏览器空闲时的任务调度。 其实,浏览器本身有对应的api:requestIdCallback。但不同浏览器执行有时间差异,不能满足react设计需要。

performUnitOfWork

function performUnitOfWork(unitOfWork) {
    var current = unitOfWork.alternate; 
    // ...
    var next;
    if ( (unitOfWork.mode & ProfileMode) !== NoMode) {
      startProfilerTimer(unitOfWork);
      next = beginWork$1(current, unitOfWork, subtreeRenderLanes);
      stopProfilerTimerIfRunningAndRecordDelta(unitOfWork, true);
    } else {
      next = beginWork$1(current, unitOfWork, subtreeRenderLanes);
    }
    resetCurrentFiber();
    unitOfWork.memoizedProps = unitOfWork.pendingProps;
    if (next === null) {
      completeUnitOfWork(unitOfWork);
    } else {
      workInProgress = next;
    }
    ReactCurrentOwner$2.current = null;
  }

profile是react内部性能跟踪调试器,在正常的开发生产环境不会主动开启,将进入beginWork阶段

三. beginWork

  function beginWork(current, workInProgress, renderLanes) {
      if (current !== null) {
      var oldProps = current.memoizedProps;
      var newProps = workInProgress.pendingProps;
      if (oldProps !== newProps || hasContextChanged() || ( // Force a re-render if the implementation changed due to hot reload:
       workInProgress.type !== current.type )) {
        didReceiveUpdate = true;
      } else if (!includesSomeLane(renderLanes, updateLanes)) {
        didReceiveUpdate = false; 
        switch (workInProgress.tag) {
          // ...
        }
      } else {
        if ((current.flags & ForceUpdateForLegacySuspense) !== NoFlags) {
          didReceiveUpdate = true;
        } else {
          didReceiveUpdate = false;
        }
      }
    } else {
      didReceiveUpdate = false;
    } 
    workInProgress.lanes = NoLanes;
    switch (workInProgress.tag) {
      case IndeterminateComponent:
        {
          return mountIndeterminateComponent(current, workInProgress, workInProgress.type, renderLanes);
        }
      case LazyComponent:
        {
          var elementType = workInProgress.elementType;
          return mountLazyComponent(current, workInProgress, elementType, updateLanes, renderLanes);
        }
      case FunctionComponent:
        {
          var _Component = workInProgress.type;
          var unresolvedProps = workInProgress.pendingProps;
          var resolvedProps = workInProgress.elementType === _Component ? unresolvedProps : resolveDefaultProps(_Component, unresolvedProps);
          return updateFunctionComponent(current, workInProgress, _Component, resolvedProps, renderLanes);
        }
      case ClassComponent:
        {
          var _Component2 = workInProgress.type;
          var _unresolvedProps = workInProgress.pendingProps;
          var _resolvedProps = workInProgress.elementType === _Component2 ? _unresolvedProps : resolveDefaultProps(_Component2, _unresolvedProps);
          return updateClassComponent(current, workInProgress, _Component2, _resolvedProps, renderLanes);
        }
      case HostRoot:
        return updateHostRoot(current, workInProgress, renderLanes);
      case HostComponent:
        return updateHostComponent(current, workInProgress, renderLanes);
      case HostText:
        return updateHostText(current, workInProgress);
      case SuspenseComponent:
        return updateSuspenseComponent(current, workInProgress, renderLanes);
      case HostPortal:
        return updatePortalComponent(current, workInProgress, renderLanes);
      case ForwardRef:
        {
          var type = workInProgress.type;
          var _unresolvedProps2 = workInProgress.pendingProps;
          var _resolvedProps2 = workInProgress.elementType === type ? _unresolvedProps2 : resolveDefaultProps(type, _unresolvedProps2);
          return updateForwardRef(current, workInProgress, type, _resolvedProps2, renderLanes);
        }
      case Fragment:
        return updateFragment(current, workInProgress, renderLanes);
      case Mode:
        return updateMode(current, workInProgress, renderLanes);
      case Profiler:
        return updateProfiler(current, workInProgress, renderLanes);
      case ContextProvider:
        return updateContextProvider(current, workInProgress, renderLanes);
      case ContextConsumer:
        return updateContextConsumer(current, workInProgress, renderLanes);
      case MemoComponent:
        {
          var _type2 = workInProgress.type;
          var _unresolvedProps3 = workInProgress.pendingProps; // Resolve outer props first, then resolve inner props.
          var _resolvedProps3 = resolveDefaultProps(_type2, _unresolvedProps3);
          {
            if (workInProgress.type !== workInProgress.elementType) {
              var outerPropTypes = _type2.propTypes;
              if (outerPropTypes) {
                checkPropTypes(outerPropTypes, _resolvedProps3, // Resolved for outer only
                'prop', getComponentName(_type2));
              }
            }
          }
          _resolvedProps3 = resolveDefaultProps(_type2.type, _resolvedProps3);
          return updateMemoComponent(current, workInProgress, _type2, _resolvedProps3, updateLanes, renderLanes);
        }
      case SimpleMemoComponent:
        {
          return updateSimpleMemoComponent(current, workInProgress, workInProgress.type, workInProgress.pendingProps, updateLanes, renderLanes);
        }
      case IncompleteClassComponent:
        {
          var _Component3 = workInProgress.type;
          var _unresolvedProps4 = workInProgress.pendingProps;
          var _resolvedProps4 = workInProgress.elementType === _Component3 ? _unresolvedProps4 : resolveDefaultProps(_Component3, _unresolvedProps4);
          return mountIncompleteClassComponent(current, workInProgress, _Component3, _resolvedProps4, renderLanes);
        }
      case SuspenseListComponent:
        {
          return updateSuspenseListComponent(current, workInProgress, renderLanes);
        }
      case FundamentalComponent:
        {
          break;
        }
      case ScopeComponent:
        {
          break;
        }
      case Block:
        {
          {
            var block = workInProgress.type;
            var props = workInProgress.pendingProps;
            return updateBlock(current, workInProgress, block, props, renderLanes);
          }
        }
      case OffscreenComponent:
        {
          return updateOffscreenComponent(current, workInProgress, renderLanes);
        }
      case LegacyHiddenComponent:
        {
          return updateLegacyHiddenComponent(current, workInProgress, renderLanes);
        }
    }
  }

其中 didReceiveUpdate会在更新ref时使用到,也是props是否有变化的标志,这里先不关注。

首次将进入rootFiber的case,即HostRoot

updateHostRoot

function updateHostRoot(current, workInProgress, renderLanes) {
  // ...
  // 服务器端渲染处理先省略...
  // ...
  reconcileChildren(current, workInProgress, nextChildren, renderLanes);
  return workInProgress.child;
}

reconcileChildren

function reconcileChildren(current, workInProgress, nextChildren, renderLanes) {
    if (current === null) {
      // If this is a fresh new component that hasn't been rendered yet, we
      // won't update its child set by applying minimal side-effects. Instead,
      // we will add them all to the child before it gets rendered. That means
      // we can optimize this reconciliation pass by not tracking side-effects.
      workInProgress.child = mountChildFibers(workInProgress, null, nextChildren, renderLanes);
    } else {
      // If the current child is the same as the work in progress, it means that
      // we haven't yet started any work on these children. Therefore, we use
      // the clone algorithm to create a copy of all the current children.
      // If we had any progressed work already, that is invalid at this point so
      // let's throw it out.
      workInProgress.child = reconcileChildFibers(workInProgress, current.child, nextChildren, renderLanes);
    }
  }

对于首次渲染,current为Null,进入reconcileChildFibers

reconcileChildFibers

function reconcileChildFibers(returnFiber, currentFirstChild, newChild, lanes) {
  var isObject = typeof newChild === 'object' && newChild !== null;
      if (isObject) {
        switch (newChild.$$typeof) {
          case REACT_ELEMENT_TYPE:
            return placeSingleChild(reconcileSingleElement(returnFiber, currentFirstChild, newChild, lanes));
          case REACT_PORTAL_TYPE:
            return placeSingleChild(reconcileSinglePortal(returnFiber, currentFirstChild, newChild, lanes));
          case REACT_LAZY_TYPE:
            {
              var payload = newChild._payload;
              var init = newChild._init; // TODO: This function is supposed to be non-recursive.
              return reconcileChildFibers(returnFiber, currentFirstChild, init(payload), lanes);
            }
        }
      }
}

这里newChild是element对象,即render初始化阶段所生成的react element。

这里,我们可以看到typeof了,除了是个symbol,防止伪造攻击外,对于不同的类型,会有不同的fiber构建流程。普通的typeof了,除了是个symbol,防止伪造攻击外,对于不同的类型,会有不同的fiber构建流程。普通的typeof了,除了是个symbol,防止伪造攻击外,对于不同的类型,会有不同的fiber构建流程。普通的typeof是element type。

reconcileSingleElement

function reconcileSingleElement(returnFiber, currentFirstChild, element, lanes) {
      var key = element.key;
      var child = currentFirstChild;
      // ... 如果存在child,递归删除
      // ...
      if (element.type === REACT_FRAGMENT_TYPE) {
        var created = createFiberFromFragment(element.props.children, returnFiber.mode, lanes, element.key);
        created.return = returnFiber;
        return created;
      } else {
        var _created4 = createFiberFromElement(element, returnFiber.mode, lanes);
        _created4.ref = coerceRef(returnFiber, currentFirstChild, element);
        _created4.return = returnFiber;
        return _created4;
      }
}

createFiberFromElement

function createFiberFromElement(element, mode, lanes) {
    var owner = null;
    {
      owner = element._owner;
    }
    var type = element.type;
    var key = element.key;
    var pendingProps = element.props;
    var fiber = createFiberFromTypeAndProps(type, key, pendingProps, owner, mode, lanes);
    {
      fiber._debugSource = element._source;
      fiber._debugOwner = element._owner;
    }
    return fiber;
  }

根据element对象创建子节点fiber树,并设置构建的子fiber.return为父fiber。遍历的方式使用的是深度优先遍历算法,一边对子节点做fiber实例化,一边对节点的上下关系做绑定

mountIndeterminateComponent

function mountIndeterminateComponent(_current, workInProgress, Component, renderLanes) {
  // ...
  prepareToReadContext(workInProgress, renderLanes);
  // ...
  setIsRendering(true);
  ReactCurrentOwner$1.current = workInProgress;
  value = renderWithHooks(null, workInProgress, Component, props, context, renderLanes);
  setIsRendering(false);
  // ...
  reconcileChildren(null, workInProgress, value, renderLanes);
  // ...
}

自定义组件,将先设置rendering状态以及全局的render fiber进行时对象,自定义组件内可能有副作用,比如useEffect,会影响flags。

renderWithHooks

function renderWithHooks(current, workInProgress, Component, props, secondArg, nextRenderLanes) {
  // ...
  {
      if (current !== null && current.memoizedState !== null) {
        ReactCurrentDispatcher$1.current = HooksDispatcherOnUpdateInDEV;
      } else if (hookTypesDev !== null) {
        ReactCurrentDispatcher$1.current = HooksDispatcherOnMountWithHookTypesInDEV;
      } else {
        ReactCurrentDispatcher$1.current = HooksDispatcherOnMountInDEV;
      }
    }
    var children = Component(props, secondArg);
    ReactCurrentDispatcher$1.current = ContextOnlyDispatcher;
}

ReactCurrentDispatcher对象很重要,这是effect处理重要的全局对象,他将改变flags值,并影响后续的effect链表构建。

Component即函数组件对象,执行的结果即element对象。此element对象将再次调用reconcileChildren,进入协调child阶段,最终返回child的Fiber。

问题来了:

  • fiber是一边生成,一边关联关系的,那么每层的fiber是如何找到下层element的?
  • 不同的fiber对象,updateQueue都一样吗?
  • 不同的fiber对象,memoizedState都一样吗?

对于rootFiber而言,updateQueue挂载的element对象,经过process update,清空updateQueue.shared,进而将element对象挂载至memoizedState上,当执行reconcileChildren时,nextChild从memoizedState获取。

对于nextChild为function组件时,包括顶层函数组件,将执行renderWithHooks,返回全量的element对象,当然renderWithHooks功能不仅仅于此,还涉及重要的flags计算。当执行reconcileChildren时,会将element挂载至下层fiber的pendingProps上。

对于nextChild为普通节点时,会根据层层根据pendingProps获取下一层节点的信息,从而继续构建fiber树。

值得注意的是,函数组件节点的updateQueue指的是lastEffect链表,他其实是一个环状链表结构。

每个节点的构建,都会设置memoizedProps = pendingProps

至此,beginWork的递归构建已完成,下面将进入completeWork,更多关于React Fiber构建beginWork的资料请关注编程网其它相关文章!

免责声明:

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

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

ReactFiber构建beginWork源码解析

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

下载Word文档

猜你喜欢

ReactFiber构建beginWork源码解析

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

ReactFiber构建源码解析

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

编程热搜

目录