React16源码: React中的performWork的源码实现

这篇具有很好参考价值的文章主要介绍了React16源码: React中的performWork的源码实现。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

performWork


1 )概述

  • performWork 涉及到在调度完成,或者同步任务进来之后整个 root 节点链条如何更新
  • 怎么更新一棵 Fiber 树,它的每一个节点是如何被遍历到,以及如何进行更新操作
  • A. 在执行 performWork 时候,是否有 deadline 的区分
    • deadline 是通过 reactschedule 它的一个时间片,更新的过程当中
    • 产生的一个叫做 deadlineobject 的对象
    • 它可以用来判断我们在一帧的渲染时间内留给react进行fiber树渲染的时间还有没有
  • B.循环渲染root的条件
    • 一个应用当中可能会有多个root节点
    • 同时每一个root节点上面呢又会有不同优先级的任务产生
    • 要循环去遍历各个不同的root节点
    • 以及他们的不同的优先级的任务,然后按照优先级去一个个去更新
    • 这个循环它如何建立,如何判断这个循环是否成立的条件
  • C.超过时间片之后的一个处理
    • 在deadline到了之后,就是我们这一帧的渲染时间已经到了
    • 我们需要把js的执行权又交回给浏览器,这个时候又该怎么做

2 )源码

定位到 packages/react-reconciler/src/ReactFiberScheduler.js文章来源地址https://www.toymoban.com/news/detail-796852.html

function performAsyncWork() {
  try {
    if (!shouldYieldToRenderer()) {
      // The callback timed out. That means at least one update has expired.
      // Iterate through the root schedule. If they contain expired work, set
      // the next render expiration time to the current time. This has the effect
      // of flushing all expired work in a single batch, instead of flushing each
      // level one at a time.
      if (firstScheduledRoot !== null) {
        recomputeCurrentRendererTime();
        let root: FiberRoot = firstScheduledRoot;
        do {
          didExpireAtExpirationTime(root, currentRendererTime);
          // The root schedule is circular, so this is never null.
          root = (root.nextScheduledRoot: any);
        } while (root !== firstScheduledRoot);
      }
    }
    performWork(NoWork, true);
  } finally {
    didYield = false;
  }
}


function performSyncWork() {
  performWork(Sync, null);
}

function performWork(minExpirationTime: ExpirationTime, dl: Deadline | null) {
  deadline = dl;

  // Keep working on roots until there's no more work, or until we reach
  // the deadline.
  findHighestPriorityRoot();

  if (deadline !== null) {
    recomputeCurrentRendererTime();
    currentSchedulerTime = currentRendererTime;

    if (enableUserTimingAPI) {
      const didExpire = nextFlushedExpirationTime < currentRendererTime;
      const timeout = expirationTimeToMs(nextFlushedExpirationTime);
      stopRequestCallbackTimer(didExpire, timeout);
    }

    while (
      nextFlushedRoot !== null &&
      nextFlushedExpirationTime !== NoWork &&
      (minExpirationTime === NoWork ||
        minExpirationTime >= nextFlushedExpirationTime) &&
      (!deadlineDidExpire || currentRendererTime >= nextFlushedExpirationTime)
    ) {
      performWorkOnRoot(
        nextFlushedRoot,
        nextFlushedExpirationTime,
        currentRendererTime >= nextFlushedExpirationTime,
      );
      findHighestPriorityRoot();
      recomputeCurrentRendererTime();
      currentSchedulerTime = currentRendererTime;
    }
  } else {
    while (
      nextFlushedRoot !== null &&
      nextFlushedExpirationTime !== NoWork &&
      (minExpirationTime === NoWork ||
        minExpirationTime >= nextFlushedExpirationTime)
    ) {
      performWorkOnRoot(nextFlushedRoot, nextFlushedExpirationTime, true);
      findHighestPriorityRoot();
    }
  }

  // We're done flushing work. Either we ran out of time in this callback,
  // or there's no more work left with sufficient priority.

  // If we're inside a callback, set this to false since we just completed it.
  if (deadline !== null) {
    callbackExpirationTime = NoWork;
    callbackID = null;
  }
  // If there's work left over, schedule a new callback.
  if (nextFlushedExpirationTime !== NoWork) {
    scheduleCallbackWithExpirationTime(
      ((nextFlushedRoot: any): FiberRoot),
      nextFlushedExpirationTime,
    );
  }

  // Clean-up.
  deadline = null;
  deadlineDidExpire = false;

  finishRendering();
}
  • performAsyncWork, performSyncWork, performWork 三个方法连在一起
  • 对于 performSyncWork 直接 调用 performWork(Sync, null); 就一行代码
  • performAsyncWork 就相对复杂,是通过 react scheduler 调度回来的
    • 参数是 deadline 对象 dl
    • 本质上只有一个 if判断 和 执行 performWork 方法
    • 在if判断中, recomputeCurrentRendererTime 这个方法不涉及主要流程,跳过
      • 在找到 root 之后,执行 didExpireaTexpirationTime 标记在root节点上的一些变量
        // packages/react-reconciler/src/ReactFiberPendingPriority.js
        export function didExpireAtExpirationTime(
          root: FiberRoot,
          currentTime: ExpirationTime,
        ): void {
          const expirationTime = root.expirationTime;
          // 有任务,但任务过期
          if (expirationTime !== NoWork && currentTime >= expirationTime) {
            // The root has expired. Flush all work up to the current time.
            root.nextExpirationTimeToWorkOn = currentTime; // 挂载 当前时间 作为 nextExpirationTimeToWorkOn 属性
          }
        }
        
      • 之后,一个do while 找最终的 root
    • 之后,执行 performWork(NoWork, dl)
      • NoWork 是 0
      • export const NoWork = 0
  • performWork
    • 有两个参数,minExpirationTime: ExpirationTime, dl: Deadline | null
    • findHighestPriorityRoot 这个方法
      function findHighestPriorityRoot() {
        let highestPriorityWork = NoWork;
        let highestPriorityRoot = null;
        // 这个if表示仍存在节点更新情况
        if (lastScheduledRoot !== null) {
          let previousScheduledRoot = lastScheduledRoot;
          let root = firstScheduledRoot;
          // 存在 root 就进行循环
          while (root !== null) {
            const remainingExpirationTime = root.expirationTime;
            // 这个if判断表示,是没有任何更新的
            if (remainingExpirationTime === NoWork) {
              // This root no longer has work. Remove it from the scheduler.
      
              // TODO: This check is redudant, but Flow is confused by the branch
              // below where we set lastScheduledRoot to null, even though we break
              // from the loop right after.
              invariant(
                previousScheduledRoot !== null && lastScheduledRoot !== null,
                'Should have a previous and last root. This error is likely ' +
                  'caused by a bug in React. Please file an issue.',
              );
              // 这种情况,只有一个 root 节点
              if (root === root.nextScheduledRoot) {
                // This is the only root in the list.
                root.nextScheduledRoot = null;
                firstScheduledRoot = lastScheduledRoot = null;
                break;
              } else if (root === firstScheduledRoot) {
                // 这时候 root 就没用了,可以删除了,获取 next
                // This is the first root in the list.
                const next = root.nextScheduledRoot;
                firstScheduledRoot = next;
                lastScheduledRoot.nextScheduledRoot = next;
                root.nextScheduledRoot = null;
              } else if (root === lastScheduledRoot) {
                // 这个时候,root是最后一个
                // This is the last root in the list.
                lastScheduledRoot = previousScheduledRoot;
                lastScheduledRoot.nextScheduledRoot = firstScheduledRoot;
                root.nextScheduledRoot = null;
                break;
              } else {
                // 移除 中间的 root.nextScheduledRoot 节点
                previousScheduledRoot.nextScheduledRoot = root.nextScheduledRoot;
                root.nextScheduledRoot = null;
              }
              root = previousScheduledRoot.nextScheduledRoot;
            } else {
              // 判断优先级,更新更高优先级
              if (
                highestPriorityWork === NoWork ||
                remainingExpirationTime < highestPriorityWork
              ) {
                // Update the priority, if it's higher
                highestPriorityWork = remainingExpirationTime;
                highestPriorityRoot = root;
              }
              if (root === lastScheduledRoot) {
                break;
              }
              if (highestPriorityWork === Sync) {
                // Sync is highest priority by definition so
                // we can stop searching.
                break;
              }
              previousScheduledRoot = root;
              root = root.nextScheduledRoot;
            }
          }
        }
        // 处理更新后的 highestPriorityRoot 和 highestPriorityWork
        nextFlushedRoot = highestPriorityRoot;
        nextFlushedExpirationTime = highestPriorityWork;
      }
      
    • 看下 deadine 不为 null 时的情况
      • 这是异步的情况
      • 主要看 while 里的 (!deadlineDidExpire || currentRendererTime >= nextFlushedExpirationTime)
        • !deadlineDidExpire 表示时间片还有剩余时间
        • nextFlushedExpirationTime 是现在要输出任务的过期时间
          • currentRendererTime >= nextFlushedExpirationTime
          • 说明 当前 render 时间 比 过期时间大,已经超时了,需要强制输出
        • 之后执行 performWorkOnRoot
          • 关于这个函数,主要关注第三个参数
          • 默认是 true
          • 对于异步情况,值为: currentRendererTime >= nextFlushedExpirationTime
            • 任务过期,则为 true, 否则为 false
        • 进入 这个函数
          function performWorkOnRoot(
            root: FiberRoot,
            expirationTime: ExpirationTime,
            isExpired: boolean, // 是否过期,这个方法里一定有针对过期任务的强制更新
          ) {
            invariant(
              !isRendering,
              'performWorkOnRoot was called recursively. This error is likely caused ' +
                'by a bug in React. Please file an issue.',
            );
          
            isRendering = true; // 注意这里和函数结束
          
            // Check if this is async work or sync/expired work.
            // deadline === null  是 Sync的情况,isExpired 是过期的情况
            if (deadline === null || isExpired) {
              // Flush work without yielding.
              // TODO: Non-yieldy work does not necessarily imply expired work. A renderer
              // may want to perform some work without yielding, but also without
              // requiring the root to complete (by triggering placeholders).
          
              let finishedWork = root.finishedWork;
              if (finishedWork !== null) {
                // This root is already complete. We can commit it.
                completeRoot(root, finishedWork, expirationTime); // 有 finishedWork 直接 completeRoot
              } else {
                root.finishedWork = null;
                // If this root previously suspended, clear its existing timeout, since
                // we're about to try rendering again.
                const timeoutHandle = root.timeoutHandle;
                // 处理 timeoutHandle 的情况,跳过
                if (timeoutHandle !== noTimeout) {
                  root.timeoutHandle = noTimeout;
                  // $FlowFixMe Complains noTimeout is not a TimeoutID, despite the check above
                  cancelTimeout(timeoutHandle);
                }
                // 在这个条件下,不可中断 因为在上层if框架下,要么是 Sync的任务,要么是 过期的任务需要立即执行
                const isYieldy = false;
                renderRoot(root, isYieldy, isExpired);
                finishedWork = root.finishedWork;
                if (finishedWork !== null) {
                  // We've completed the root. Commit it.
                  completeRoot(root, finishedWork, expirationTime);
                }
              }
            } else {
              // 这里匹配 Async 异步任务,和上面Sync的任务流程基本差不多
              // Flush async work.
              let finishedWork = root.finishedWork;
              // 一开始进来判断这个 finishedWork,有可能在上一个时间片中,renderRoot执行完了,但是,没有时间去执行 completeRoot 了
              // 需要再下次异步调度的时候进来,如果有 finishedWork 则先 completeRoot
              if (finishedWork !== null) {
                // This root is already complete. We can commit it.
                completeRoot(root, finishedWork, expirationTime);
              } else {
                root.finishedWork = null;
                // If this root previously suspended, clear its existing timeout, since
                // we're about to try rendering again.
                const timeoutHandle = root.timeoutHandle;
                if (timeoutHandle !== noTimeout) {
                  root.timeoutHandle = noTimeout;
                  // $FlowFixMe Complains noTimeout is not a TimeoutID, despite the check above
                  cancelTimeout(timeoutHandle);
                }
                // 这个任务是可以中断的
                const isYieldy = true;
                renderRoot(root, isYieldy, isExpired);
                finishedWork = root.finishedWork;
                // finishedWork 有可能为 null, 中断了就没有完成任务
                if (finishedWork !== null) {
                  // We've completed the root. Check the deadline one more time
                  // before committing.
                  // 先判断是否需要跳出,这时候时间片可能已经用完,如果没有用完,执行 completeRoot
                  if (!shouldYield()) {
                    // Still time left. Commit the root.
                    completeRoot(root, finishedWork, expirationTime);
                  } else {
                    // There's no time left. Mark this root as complete. We'll come
                    // back and commit it later.
                    // 需要跳出,则赋值 finishedWork,注意这里不执行 completeRoot,因为没有时间了,需要等待下个时间片进来才能执行
                    root.finishedWork = finishedWork;
                  }
                }
              }
            }
          
            isRendering = false;
          }
          
        • 执行 findHighestPriorityRoot
        • currentSchedulerTime = currentRendererTime
    • 看下 deadine 为 null 时的情况
      • 这是同步的情况
      • while(nextFlushedRoot !== null && nextFlushedExpirationTime !== NoWork && (minExpirationTime === NoWork || minExpirationTime >= nextFlushedExpirationTime))
        • 对于 perfromSyncWork 来说,minExpirationTime 是 1,1 >= nextFlushedExpirationTime 说明 只有 Sync(1)的情况,或者 NoWork,所以 nextFlushedExpirationTime 只有是1的情况
        • 相当于在 perfromSyncWork 的时候,只会执行 root.expirationTime 是 Sync 的任务,也就是说是同步更新的更新,才会在这里继续执行,这样和 SyncWork 这函数名匹配
        • 在这种情况下,调用 performWorkOnRootfindHighestPriorityRoot 执行掉 Sync的任务
  • 注意,在 performWork 上两个循环的判断条件
  • 以及传入 performWorkOnRoot的第三个参数的意义

到了这里,关于React16源码: React中的performWork的源码实现的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • React16源码: React中的unwindWork的源码实现

    unwindWork 1 )概述 在 renderRoot 的 throw Exception 里面, 对于被捕获到错误的组件进行了一些处理 并且向上去寻找能够处理这些异常的组件,比如说 class component 里面具有 getDerivedStateFromError 或者 componentDidCatch 这样的生命周期方法 这个class component 就代表它可以处理它的子树当中渲

    2024年01月25日
    浏览(37)
  • React16源码: React中的updateClassComponent的源码实现

    ClassComponent 的更新 1 ) 概述 在 react 中 class component,是一个非常重要的角色 它承担了 react 中 更新整个应用的API setState forceUpdate 在react当中,只有更新了state之后,整个应用才会重新进行渲染 在 class component 中, 它的逻辑相对复杂 2 )源码 在 packages/react-reconciler/src/ReactFiberB

    2024年01月21日
    浏览(40)
  • React16源码: React中的completeUnitOfWork的源码实现

    completeUnitOfWork 1 )概述 各种不同类型组件的一个更新过程对应的是在执行 performUnitOfWork 里面的 beginWork 阶段 它是去向下遍历一棵 fiber 树的一侧的子节点,然后遍历到叶子节点为止,以及 return 自己 child 的这种方式 在 performUnitOfWork 里面,还有一个方法叫做 completeUnitOfWork 在

    2024年01月23日
    浏览(44)
  • React16源码: React中的renderRoot的源码实现

    renderRoot 1 )概述 renderRoot 是一个非常复杂的方法 这个方法里处理很多各种各样的逻辑, 它主要的工作内容是什么? A. 它调用 workLoop 进行循环单元更新 遍历整个 Fiber Tree,把每一个组件或者 dom 节点对应的 Fiber 节点拿出来单一的进行更新,这是一个循环的操作 把整棵 Fiber T

    2024年01月17日
    浏览(43)
  • React16源码: React中的setState和forceUpdate源码实现

    setState 和 forceUpdate 1 ) 概述 通过 class component 内部的 setState ,以及 forceUpdate 去更新一个组件的过程 在react的应用当中,我们只有 ReactDOM.render setState ,以及 forceUpdate 这几种种方式去更新react的应用是合理的,其他没有什么特别常用的方式去更新了 而且react官方推荐的也是用

    2024年01月25日
    浏览(40)
  • React16源码: React中的HostComponent & HostText的源码实现

    HostComponent HostText 1 )概述 HostComponent 就是我们dom原生的这些节点, 如: div, span, p 标签这种 使用的是小写字母开头的这些节点一般都认为它是一个 HostComponent HostText ,它是单纯的文本节点 主要关注它们的一个更新过程 2 )源码 定位到 packages/react-reconciler/src/ReactFiberBeginWork.js 进

    2024年01月24日
    浏览(41)
  • React16源码: React中的不同的expirationTime的源码实现

    不同的 expirationTime 1 )概述 在React中不仅仅有异步任务 大部分情况下都是同步的任务,所以会有不同 expirationTime 的存在 2 )种类 A. Sync 模式,优先级最高 任务创建完成之后,立马更新到真正的dom里面 是一个创建即更新的流程 B. Async 模式, 异步模式 会有一个调度 包含一系列

    2024年02月01日
    浏览(38)
  • React16源码: React中的update和updateQueue的源码实现

    React中的update和updateQueue 1 )概述 在 ReactDOM.render 过程中,还需要创建一个 update 对象 update 用于记录组件状态的改变的一个对象,它存放于Fiber对象的 updateQueue 中 updateQueue ,它是一个单向链表的结构,一次整体的更新过程当中 可能在这个queue里会存在多 Update 在这次更新的过

    2024年02月02日
    浏览(38)
  • React16源码: React中的PortalComponent创建, 调和, 更新的源码实现

    PortalComponent 1 )概述 React Portal之所以叫Portal,因为做的就是和“传送门”一样的事情 render到一个组件里面去,实际改变的是网页上另一处的DOM结构 主要关注 portal的创建, 调和, 更新过程 2 )源码 定位到 packages/react-dom/src/client/ReactDOM.js#L576 这里调用的是 ReactPortal.createPortal ,

    2024年01月21日
    浏览(42)
  • React16源码: React中的reconcileChildIterator和reconcileChildrenArray的源码实现

    reconcileChildIterator 和 reconcileChildrenArray 1 )概述 在react更新某一个节点的时候,要根据这个节点,它的类型去获取它的children 比如说如果是 Function Component,它要调用这个 component 计算出它的return的属性 return的属性可能是一个数组,可能是单个的 ReactElement,可能是 number, string

    2024年01月20日
    浏览(36)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包