搜尋
首頁web前端js教程React16.2的fiber架構詳解

React16.2的fiber架構詳解

Feb 03, 2018 pm 02:23 PM
fiber詳解

本文主要和大家分享React16.2的fiber架构详解,希望能帮助到大家。insertUpdateIntoFiber 会根据fiber的状态创建一个或两个列队对象,对象是长成这样的对象是长成这样的

//by 司徒正美, 加群:370262116 一起研究React与anujs
// https://github.com/RubyLouvre/anu 欢迎加star
function createUpdateQueue(baseState) {//我们现在是丢了一个null做传参
  var queue = {
    baseState: baseState,
    expirationTime: NoWork,//NoWork会被立即执行
    first: null,
    last: null,
    callbackList: null,
    hasForceUpdate: false,
    isInitialized: false
  };

  return queue;
}

scheduleWork是一个奇怪的方法,只是添加一下参数

 function scheduleWork(fiber, expirationTime) {
    return scheduleWorkImpl(fiber, expirationTime, false);
  }

scheduleWorkImpl的最开头有一个recordScheduleUpdate方法,用来记录调度器的执行状态,如注释所示,它现在相当于什么都没有做

function recordScheduleUpdate() {
  if (enableUserTimingAPI) {//全局变量,默认为true
    if (isCommitting) {//全局变量,默认为false, 没有进入分支
      hasScheduledUpdateInCurrentCommit = true;
    }
    //全局变量,默认为null,没有没有进入分支
    if (currentPhase !== null && currentPhase !== 'componentWillMount' && currentPhase !== 'componentWillReceiveProps') {
      hasScheduledUpdateInCurrentPhase = true;
    }
  }
}

scheduleWorkImpl的一些分支非常复杂,我们打一些断点

function computeExpirationForFiber(fiber) {
    var expirationTime = void 0;
    if (expirationContext !== NoWork) {
      // An explicit expiration context was set;
      expirationTime = expirationContext;
    } else if (isWorking) {
      if (isCommitting) {
        // Updates that occur during the commit phase should have sync priority
        // by default.
        expirationTime = Sync;
      } else {
        // Updates during the render phase should expire at the same time as
        // the work that is being rendered.
        expirationTime = nextRenderExpirationTime;
      }
    } else {
      // No explicit expiration context was set, and we're not currently
      // performing work. Calculate a new expiration time.
      if (useSyncScheduling && !(fiber.internalContextTag & AsyncUpdates)) {
        // This is a sync update
        console.log("expirationTime", Sync)
        expirationTime = Sync;//命中这里
      } else {
        // This is an async update
        expirationTime = computeAsyncExpiration();
      }
    }
    return expirationTime;
  }
    function checkRootNeedsClearing(root, fiber, expirationTime) {
    if (!isWorking && root === nextRoot && expirationTime  expirationTime) {
        node.expirationTime = expirationTime;//由于默认就是NoWork,因此会被重写 Sync
      }
      if (node.alternate !== null) {//这里进不去
        if (node.alternate.expirationTime === NoWork || node.alternate.expirationTime > expirationTime) {
          node.alternate.expirationTime = expirationTime;
        }
      }
      if (node['return'] === null) {
        if (node.tag === HostRoot) {//进入这里
          var root = node.stateNode;
          checkRootNeedsClearing(root, fiber, expirationTime);
          console.log("requestWork",root, expirationTime)
          requestWork(root, expirationTime);
          checkRootNeedsClearing(root, fiber, expirationTime);
        } else {
          return;
        }
      }
      node = node['return'];
    }
  }

输出如下

React16.2的fiber架構詳解

requestWork也很难理解,里面太多全局变量,觉得不是前端的人搞的。为了帮助理解,我们继续加日志

//by 司徒正美, 加群:370262116 一起研究React与anujs

 // requestWork is called by the scheduler whenever a root receives an update.
  // It's up to the renderer to call renderRoot at some point in the future.
  /*
只要root收到更新(update对象),requestWork就会被调度程序调用。
渲染器在将来的某个时刻调用renderRoot。
  */
  function requestWork(root, expirationTime) {
    if (nestedUpdateCount > NESTED_UPDATE_LIMIT) {
      invariant_1(false, 'Maximum update depth exceeded. This can happen when a component repeatedly calls setState inside componentWillUpdate or componentDidUpdate. React limits the number of nested updates to prevent infinite loops.');
    }

    // Add the root to the schedule.
    // Check if this root is already part of the schedule.
    if (root.nextScheduledRoot === null) {
      // This root is not already scheduled. Add it.
      console.log("设置remainingExpirationTime",expirationTime)
      root.remainingExpirationTime = expirationTime;
      if (lastScheduledRoot === null) {
        console.log("设置firstScheduledRoot, lastScheduledRoot")
        firstScheduledRoot = lastScheduledRoot = root;
        root.nextScheduledRoot = root;
      } else {
        lastScheduledRoot.nextScheduledRoot = root;
        lastScheduledRoot = root;
        lastScheduledRoot.nextScheduledRoot = firstScheduledRoot;
      }
    } else {
      // This root is already scheduled, but its priority may have increased.
      var remainingExpirationTime = root.remainingExpirationTime;
      if (remainingExpirationTime === NoWork || expirationTime <p>从日志输出来看,requestWork只是修改了两个全局变量,然后进入performWork。这三个内部方法起名很有意思。scheduleWork意为<code>打算工作</code>,requestWork意为<code>申请工作</code>,performWork意为<code>努力工作(正式上班)</code></p><pre class="brush:php;toolbar:false">function performWork(minExpirationTime, dl) {
    deadline = dl;

    // Keep working on roots until there's no more work, or until the we reach
    // the deadline.
    //这里会将root设置为highestPriorityRoot
    findHighestPriorityRoot();

    if (enableUserTimingAPI && deadline !== null) {
      var didExpire = nextFlushedExpirationTime <p><span class="img-wrap"><img  src="/static/imghwm/default1.png" data-src="https://img.php.cn/upload/article/000/054/025/944dc74785d9d477fd8444f5658d976d-1.png?x-oss-process=image/resize,p_40" class="lazy" alt="React16.2的fiber架構詳解" ></span></p><p>我们终于进入performWorkOnRoot,performWorkOnRoot的作用是区分同步渲染还是异步渲染,expirationTime等于1,因此进入同步。导步肯定为false</p><pre class="brush:php;toolbar:false">// https://github.com/RubyLouvre/anu 欢迎加star

function performWorkOnRoot(root, expirationTime) {

    isRendering = true;

    // Check if this is async work or sync/expired work.
    // TODO: Pass current time as argument to renderRoot, commitRoot
    if (expirationTime <p><span class="img-wrap"><img  src="/static/imghwm/default1.png" data-src="https://img.php.cn/upload/article/000/054/025/8f77c2afe11e03546d57c29e697ca86f-2.png?x-oss-process=image/resize,p_40" class="lazy" alt="React16.2的fiber架構詳解" ></span></p><p>renderRoot也是怒长,React16代码的特点是许多巨型类,巨型方法,有JAVA之遗风。renderRoot只有前面几行是可能处理虚拟DOM(或叫fiber),后面都是错误边界的</p><pre class="brush:php;toolbar:false">function renderRoot(root, expirationTime) {
   
    isWorking = true;

    // We're about to mutate the work-in-progress tree. If the root was pending
    // commit, it no longer is: we'll need to complete it again.
    root.isReadyForCommit = false;

    // Check if we're starting from a fresh stack, or if we're resuming from
    // previously yielded work.
    if (root !== nextRoot || expirationTime !== nextRenderExpirationTime || nextUnitOfWork === null) {
      // Reset the stack and start working from the root.
      resetContextStack();
      nextRoot = root;
      nextRenderExpirationTime = expirationTime;
      //可能是用来工作的代码
       console.log("createWorkInProgress")
      nextUnitOfWork = createWorkInProgress(nextRoot.current, null, expirationTime);
    }
    //可能是用来工作的代码
     console.log("startWorkLoopTimer")
    startWorkLoopTimer(nextUnitOfWork);
   // 处理错误边界
    var didError = false;
    var error = null;
    invokeGuardedCallback$1(null, workLoop, null, expirationTime);
    // An error was thrown during the render phase.
    while (didError) {
       console.log("componentDidCatch的相关实现")
      if (didFatal) {
        // This was a fatal error. Don't attempt to recover from it.
        firstUncaughtError = error;
        break;
      }

      var failedWork = nextUnitOfWork;
      if (failedWork === null) {
        // An error was thrown but there's no current unit of work. This can
        // happen during the commit phase if there's a bug in the renderer.
        didFatal = true;
        continue;
      }

      // 处理错误边界
      var boundary = captureError(failedWork, error);
      !(boundary !== null) ? invariant_1(false, 'Should have found an error boundary. This error is likely caused by a bug in React. Please file an issue.') : void 0;

      if (didFatal) {
        // The error we just captured was a fatal error. This happens
        // when the error propagates to the root more than once.
        continue;
      }
       // 处理错误边界
      didError = false;
      error = null;
      // We're finished working. Exit the error loop.
      break;
    }
   // 处理错误边界
    var uncaughtError = firstUncaughtError;

    // We're done performing work. Time to clean up.
    stopWorkLoopTimer(interruptedBy);
    interruptedBy = null;
    isWorking = false;
    didFatal = false;
    firstUncaughtError = null;
     // 处理错误边界
    if (uncaughtError !== null) {
      onUncaughtError(uncaughtError);
    }

    return root.isReadyForCommit ? root.current.alternate : null;
  }

  function resetContextStack() {
    // Reset the stack
    reset$1();
    // Reset the cursors
    resetContext();
    resetHostContainer();
  }

function reset$1() {
  console.log("reset",index)
  while (index > -1) {
    valueStack[index] = null;

    {
      fiberStack[index] = null;
    }

    index--;
  }
}

function resetContext() {
  consoel.log("resetContext")
  previousContext = emptyObject_1;
  contextStackCursor.current = emptyObject_1;
  didPerformWorkStackCursor.current = false;
}

  function resetHostContainer() {
    console.log("resetHostContainer",contextStackCursor, rootInstanceStackCursor, NO_CONTEXT )
    contextStackCursor.current = NO_CONTEXT;
    rootInstanceStackCursor.current = NO_CONTEXT;
  }

React16.2的fiber架構詳解

createWorkInProgress就是将根组件的fiber对象再复制一份,变成其alternate属性。因此 将虚拟DOM转换为真实DOM的重任就交给invokeGuardedCallback

var invokeGuardedCallback = function (name, func, context, a, b, c, d, e, f) {
  ReactErrorUtils._hasCaughtError = false;
  ReactErrorUtils._caughtError = null;
  var funcArgs = Array.prototype.slice.call(arguments, 3);
  try {
    func.apply(context, funcArgs);
  } catch (error) {
    ReactErrorUtils._caughtError = error;
    ReactErrorUtils._hasCaughtError = true;
  }
//这下面还有怒长(100-150L )的关于错误边界的处理,略过
};

func为workLoop

//by 司徒正美, 加群:370262116 一起研究React与anujs

 function workLoop(expirationTime) {
    if (capturedErrors !== null) {
      // If there are unhandled errors, switch to the slow work loop.
      // TODO: How to avoid this check in the fast path? Maybe the renderer
      // could keep track of which roots have unhandled errors and call a
      // forked version of renderRoot.
      slowWorkLoopThatChecksForFailedWork(expirationTime);
      return;
    }
    if (nextRenderExpirationTime === NoWork || nextRenderExpirationTime > expirationTime) {
      return;
    }

    if (nextRenderExpirationTime <p><span class="img-wrap"><img  src="/static/imghwm/default1.png" data-src="https://img.php.cn/upload/article/000/054/025/ac8d1569f29d1205551aecca3c3e6573-4.png?x-oss-process=image/resize,p_40" class="lazy" alt="React16.2的fiber架構詳解" ></span><br>我们终于看到工作的代码了。 这个nextUnitOfWork 是renderRoot生成的<br>performUnitOfWork与beginWork的代码,里面会根据fiber的tag进入各种操作</p><pre class="brush:php;toolbar:false">//by 司徒正美, 加群:370262116 一起研究React与anujs
// https://github.com/RubyLouvre/anu 欢迎加star

function performUnitOfWork(workInProgress) {
    // The current, flushed, state of this fiber is the alternate.
    // Ideally nothing should rely on this, but relying on it here
    // means that we don't need an additional field on the work in
    // progress.
    var current = workInProgress.alternate;

    // See if beginning this work spawns more work.
    startWorkTimer(workInProgress);
    {
      ReactDebugCurrentFiber.setCurrentFiber(workInProgress);
    }
    console.log("beginWork")
    var next = beginWork(current, workInProgress, nextRenderExpirationTime);
    {
      ReactDebugCurrentFiber.resetCurrentFiber();
    }
    if (true && ReactFiberInstrumentation_1.debugTool) {
      ReactFiberInstrumentation_1.debugTool.onBeginWork(workInProgress);
    }

    if (next === null) {
      console.log("next")
      // If this doesn't spawn new work, complete the current work.
      next = completeUnitOfWork(workInProgress);
    }

    ReactCurrentOwner.current = null;

    return next;
  }
function beginWork(current, workInProgress, renderExpirationTime) {
    if (workInProgress.expirationTime === NoWork || workInProgress.expirationTime > renderExpirationTime) {
      return bailoutOnLowPriority(current, workInProgress);
    }

    switch (workInProgress.tag) {
      case IndeterminateComponent:
        return mountIndeterminateComponent(current, workInProgress, renderExpirationTime);
      case FunctionalComponent:
        return updateFunctionalComponent(current, workInProgress);
      case ClassComponent:
        return updateClassComponent(current, workInProgress, renderExpirationTime);
      case HostRoot:
        return updateHostRoot(current, workInProgress, renderExpirationTime);
      case HostComponent:
        return updateHostComponent(current, workInProgress, renderExpirationTime);
      case HostText:
        return updateHostText(current, workInProgress);
      case CallHandlerPhase:
        // This is a restart. Reset the tag to the initial phase.
        workInProgress.tag = CallComponent;
      // Intentionally fall through since this is now the same.
      case CallComponent:
        return updateCallComponent(current, workInProgress, renderExpirationTime);
      case ReturnComponent:
        // A return component is just a placeholder, we can just run through the
        // next one immediately.
        return null;
      case HostPortal:
        return updatePortalComponent(current, workInProgress, renderExpirationTime);
      case Fragment:
        return updateFragment(current, workInProgress);
      default:
        invariant_1(false, 'Unknown unit of work tag. This error is likely caused by a bug in React. Please file an issue.');
    }
  }

我们再调查一下workInProgress.tag是什么

https://github.com/facebook/r...

这里有全部fiber节点的类型描述,我们创建一个对象

// https://github.com/RubyLouvre/anu 欢迎加star

var mapBeginWork = {
    3: "HostRoot 根组件",
    0: "IndeterminateComponent 只知道type为函数",
    2: "ClassComponent 普通类组件" ,
    5: "HostComponent 元素节点",
    6: "HostText 文本节点"
  }
  function beginWork(current, workInProgress, renderExpirationTime) {
    if (workInProgress.expirationTime === NoWork || workInProgress.expirationTime > renderExpirationTime) {
      return bailoutOnLowPriority(current, workInProgress);
    }
    console.log(workInProgress.tag, mapBeginWork[workInProgress.tag])
     switch (workInProgress.tag) {
     //略
     }
}

React16.2的fiber架構詳解

相关推荐:

nodejs中的fiber(纤程)库详解_node.js


以上是React16.2的fiber架構詳解的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
JavaScript的演變:當前的趨勢和未來前景JavaScript的演變:當前的趨勢和未來前景Apr 10, 2025 am 09:33 AM

JavaScript的最新趨勢包括TypeScript的崛起、現代框架和庫的流行以及WebAssembly的應用。未來前景涵蓋更強大的類型系統、服務器端JavaScript的發展、人工智能和機器學習的擴展以及物聯網和邊緣計算的潛力。

神秘的JavaScript:它的作用以及為什麼重要神秘的JavaScript:它的作用以及為什麼重要Apr 09, 2025 am 12:07 AM

JavaScript是現代Web開發的基石,它的主要功能包括事件驅動編程、動態內容生成和異步編程。 1)事件驅動編程允許網頁根據用戶操作動態變化。 2)動態內容生成使得頁面內容可以根據條件調整。 3)異步編程確保用戶界面不被阻塞。 JavaScript廣泛應用於網頁交互、單頁面應用和服務器端開發,極大地提升了用戶體驗和跨平台開發的靈活性。

Python還是JavaScript更好?Python還是JavaScript更好?Apr 06, 2025 am 12:14 AM

Python更适合数据科学和机器学习,JavaScript更适合前端和全栈开发。1.Python以简洁语法和丰富库生态著称,适用于数据分析和Web开发。2.JavaScript是前端开发核心,Node.js支持服务器端编程,适用于全栈开发。

如何安裝JavaScript?如何安裝JavaScript?Apr 05, 2025 am 12:16 AM

JavaScript不需要安裝,因為它已內置於現代瀏覽器中。你只需文本編輯器和瀏覽器即可開始使用。 1)在瀏覽器環境中,通過標籤嵌入HTML文件中運行。 2)在Node.js環境中,下載並安裝Node.js後,通過命令行運行JavaScript文件。

在Quartz中如何在任務開始前發送通知?在Quartz中如何在任務開始前發送通知?Apr 04, 2025 pm 09:24 PM

如何在Quartz中提前發送任務通知在使用Quartz定時器進行任務調度時,任務的執行時間是由cron表達式設定的。現�...

在JavaScript中,如何在構造函數中獲取原型鏈上函數的參數?在JavaScript中,如何在構造函數中獲取原型鏈上函數的參數?Apr 04, 2025 pm 09:21 PM

在JavaScript中如何獲取原型鏈上函數的參數在JavaScript編程中,理解和操作原型鏈上的函數參數是常見且重要的任�...

微信小程序webview中Vue.js動態style位移失效是什麼原因?微信小程序webview中Vue.js動態style位移失效是什麼原因?Apr 04, 2025 pm 09:18 PM

在微信小程序web-view中使用Vue.js動態style位移失效的原因分析在使用Vue.js...

在Tampermonkey中如何實現對多個鏈接的並發GET請求並依次判斷返回結果?在Tampermonkey中如何實現對多個鏈接的並發GET請求並依次判斷返回結果?Apr 04, 2025 pm 09:15 PM

在Tampermonkey中如何對多個鏈接進行並發GET請求並依次判斷返回結果?在Tampermonkey腳本中,我們經常需要對多個鏈...

See all articles

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

AI Hentai Generator

AI Hentai Generator

免費產生 AI 無盡。

熱門文章

R.E.P.O.能量晶體解釋及其做什麼(黃色晶體)
3 週前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳圖形設置
3 週前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.如果您聽不到任何人,如何修復音頻
3 週前By尊渡假赌尊渡假赌尊渡假赌
WWE 2K25:如何解鎖Myrise中的所有內容
3 週前By尊渡假赌尊渡假赌尊渡假赌

熱工具

Atom編輯器mac版下載

Atom編輯器mac版下載

最受歡迎的的開源編輯器

SAP NetWeaver Server Adapter for Eclipse

SAP NetWeaver Server Adapter for Eclipse

將Eclipse與SAP NetWeaver應用伺服器整合。

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

SecLists

SecLists

SecLists是最終安全測試人員的伙伴。它是一個包含各種類型清單的集合,這些清單在安全評估過程中經常使用,而且都在一個地方。 SecLists透過方便地提供安全測試人員可能需要的所有列表,幫助提高安全測試的效率和生產力。清單類型包括使用者名稱、密碼、URL、模糊測試有效載荷、敏感資料模式、Web shell等等。測試人員只需將此儲存庫拉到新的測試機上,他就可以存取所需的每種類型的清單。

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用