国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

React16源碼: React中的completeUnitOfWork的源碼實(shí)現(xiàn)

這篇具有很好參考價(jià)值的文章主要介紹了React16源碼: React中的completeUnitOfWork的源碼實(shí)現(xiàn)。希望對(duì)大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請(qǐng)大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問(wèn)。

completeUnitOfWork


1 )概述

  • 各種不同類型組件的一個(gè)更新過(guò)程對(duì)應(yīng)的是在執(zhí)行 performUnitOfWork 里面的 beginWork 階段
  • 它是去向下遍歷一棵 fiber 樹(shù)的一側(cè)的子節(jié)點(diǎn),然后遍歷到葉子節(jié)點(diǎn)為止,以及 return 自己 child 的這種方式
  • performUnitOfWork 里面,還有一個(gè)方法叫做 completeUnitOfWork
  • completeUnitOfWork 中會(huì)根據(jù)是否有中斷,調(diào)用不同的一個(gè)處理方法
  • 什么叫中斷呢?
    • 就是我們?cè)阡秩疽粋€(gè)組件的時(shí)候,出現(xiàn)了錯(cuò)誤的場(chǎng)景進(jìn)行處理
    • 或者優(yōu)先級(jí)被更高優(yōu)先級(jí)打斷,低優(yōu)先級(jí)轉(zhuǎn)移到下一次處理
  • renderRoot 當(dāng)中, 使用try catch去調(diào)用 workLoop 去循環(huán)每一個(gè)節(jié)點(diǎn)的
  • workLoop 里面調(diào)用的就是 performUnitOfWork
  • performUnitOfWork 里面調(diào)用了 beginWork
  • 它里面就是執(zhí)行各種不同組件的一個(gè)update的一個(gè)過(guò)程
  • 在后面 completeUnitOfWork ,是處理到一側(cè)的子節(jié)點(diǎn)遍歷到最下層的時(shí)候,它沒(méi)有子節(jié)點(diǎn)可以返回了
  • 因?yàn)?beginWork 里面調(diào)用了各種update,它們都是return自己的child
  • 比如說(shuō),有這么一棵 fiber 樹(shù)的一個(gè)情況的時(shí)候
React16源碼: React中的completeUnitOfWork的源碼實(shí)現(xiàn),React | React Native,react.js,前端,前端框架
  • 我們先去對(duì) RootFiber 執(zhí)行了 beginWork,然后執(zhí)行了之后 return 的是它的 child,也就是 App
  • 一層一層這么執(zhí)行下來(lái)之后,到了有分叉節(jié)點(diǎn),也就是說(shuō)一個(gè)節(jié)點(diǎn),下面有多個(gè)子節(jié)點(diǎn)的情況
  • 它 return 的還是它的第一個(gè) child 節(jié)點(diǎn),所以這個(gè)時(shí)候,后面的節(jié)點(diǎn)是沒(méi)有被執(zhí)行到更新的
  • 因?yàn)?inpput 它返回了之后,返回的是它里面的 input child 這個(gè)節(jié)點(diǎn)
  • 到這里為止,這個(gè) dom 的 input 節(jié)點(diǎn),已經(jīng)沒(méi)有child了,這個(gè)時(shí)候就 return null
  • 因?yàn)樗?child是 null,所以在這里首先執(zhí)行的是 beginWork
  • 如果有child,它就會(huì)一直往child方向進(jìn)行一個(gè)查找
  • 等到 next 等于 null 的情況的時(shí)候,就會(huì)去執(zhí)行 completeUnitOfWork
  • completeUnitOfWork 就是對(duì)當(dāng)前這個(gè)input界定 執(zhí)行它的complete,具體執(zhí)行什么
  • beginWork 的過(guò)程當(dāng)中,如果我們?nèi)pdate某一個(gè)節(jié)點(diǎn)的時(shí)候,有報(bào)錯(cuò)了
  • 或者是 throw 了一個(gè) promise, 就是 suspend 的情況, 它會(huì)執(zhí)行一定的標(biāo)記
  • renderRoot 當(dāng)中,我們可以看到它如果有報(bào)錯(cuò),它的catch是要在 renderRoot 里面被捕獲的這個(gè)錯(cuò)誤
  • 對(duì)這個(gè)錯(cuò)誤補(bǔ)獲之后,它是有一定的處理的, 處理完之后,它仍然處于這個(gè) while 循環(huán)當(dāng)中,它里面并沒(méi)有 break
    • 位置: packages/react-reconciler/src/ReactFiberScheduler.js#L1276
  • 這個(gè)時(shí)候它還是會(huì)往下進(jìn)行一個(gè)執(zhí)行的,只不過(guò)它會(huì)在里面給我們當(dāng)前這個(gè)節(jié)點(diǎn)去執(zhí)行一個(gè)標(biāo)記
  • completeUnitOfWork 的階段,會(huì)對(duì)有各種不同標(biāo)記的一個(gè)節(jié)點(diǎn),執(zhí)行不同的方法
  • 主要的是我們正常流程走的是叫 completeWork 這么一個(gè)方法
  • 而對(duì)于有異常的節(jié)點(diǎn),它的一個(gè)流程是調(diào)用一個(gè)叫 unwindWork 的方法
  • 然后會(huì)判斷是否有兄弟節(jié)點(diǎn)來(lái)執(zhí)行不同的操作
    • 比如執(zhí)行到上圖 input 這個(gè)節(jié)點(diǎn)的 completeUnitOfWork之后,它沒(méi)有兄弟節(jié)點(diǎn)
    • 它直接去執(zhí)行它的父節(jié)點(diǎn),也就是 Input 組件的 completeUnitOfWork
    • 在這里,我們發(fā)現(xiàn)它是有兄弟節(jié)點(diǎn)的,因?yàn)榈谝淮蝩pdate的過(guò)程,是往下的
    • 對(duì)于旁邊的 List 節(jié)點(diǎn)是沒(méi)有任何更新的, 這個(gè)時(shí)候我們就會(huì)返回它的兄弟節(jié)點(diǎn) List
  • 對(duì)于這個(gè)兄弟節(jié)點(diǎn), 繼續(xù)對(duì)它執(zhí)行一個(gè)update的一個(gè)過(guò)程,也就是 beginWork 的一個(gè)過(guò)程
  • 這就是在 completeUnitOfWork 里面非常重要的一個(gè)判斷條件
    • 就是如果有兄弟節(jié)點(diǎn),它會(huì)直接返回這個(gè)兄弟節(jié)點(diǎn)
  • 完成節(jié)點(diǎn)之后要賦值整個(gè)effect鏈
    • effect是非常重要的,后面即將要把所有的更新結(jié)束的節(jié)點(diǎn),掛載到真正的dom上面
    • 這一個(gè)階段叫做commit階段,它要去執(zhí)行每一個(gè)節(jié)點(diǎn)不同的跟dom有關(guān)的操作
    • 在前面 beginWorkcompleteUnitOfWork,對(duì)每一個(gè)節(jié)點(diǎn)的更新過(guò)程標(biāo)記了
    • 有哪些 SideEffect 最終要被commit的一個(gè)過(guò)程
    • completeUnitOfWork 里面會(huì)完成這一步,把所有的effect節(jié)點(diǎn)進(jìn)行一個(gè)串聯(lián)
    • commitWork 的階段可以非常方便的根據(jù)這個(gè)鏈去執(zhí)行每一個(gè)節(jié)點(diǎn)的最終的操作

2 ) 源碼

定位到 packages/react-reconciler/src/ReactFiberScheduler.js#L939
找到 completeUnitOfWork文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-817779.html

// 這個(gè)API 是在 workInProgress 的基礎(chǔ)上進(jìn)行的一個(gè)操作
function completeUnitOfWork(workInProgress: Fiber): Fiber | null {
  // Attempt to complete the current unit of work, then move to the
  // next sibling. If there are no more siblings, return to the
  // parent fiber.

  // 進(jìn)來(lái)首先就是一個(gè) while true 的一個(gè)循環(huán)
  while (true) {
    // 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.
    // 對(duì)于這個(gè)循環(huán), 它首先獲取current
    const current = workInProgress.alternate;
    if (__DEV__) {
      ReactCurrentFiber.setCurrentFiber(workInProgress);
    }
    // 然后 獲取 returnFiber 和 siblingFiber,也就是它的父節(jié)點(diǎn)以及它的兄弟節(jié)點(diǎn)
    // 這個(gè)在后面我們判斷是否要返回兄弟節(jié)點(diǎn)的時(shí)候就會(huì)用到
    const returnFiber = workInProgress.return;
    const siblingFiber = workInProgress.sibling;

    // 這里首先一進(jìn)來(lái)就有一個(gè)大的判斷,它是一個(gè)整個(gè)方法里面最大的一個(gè)判斷
    // Incomplete 就是這個(gè)節(jié)點(diǎn)它是出現(xiàn)了錯(cuò)誤,然后被捕獲的, 并標(biāo)記這個(gè) sideEffect
    // 邏輯與操作來(lái)判斷某一個(gè)屬性上面它是否有某一個(gè)特性的一個(gè)方式
    if ((workInProgress.effectTag & Incomplete) === NoEffect) {
      if (__DEV__ && replayFailedUnitOfWorkWithInvokeGuardedCallback) {
        // Don't replay if it fails during completion phase.
        mayReplayFailedUnitOfWork = false;
      }
      // This fiber completed.
      // Remember we're completing this unit so we can find a boundary if it fails.
      nextUnitOfWork = workInProgress;
      if (enableProfilerTimer) {
        if (workInProgress.mode & ProfileMode) {
          startProfilerTimer(workInProgress);
        }
        nextUnitOfWork = completeWork(
          current,
          workInProgress,
          nextRenderExpirationTime,
        );
        if (workInProgress.mode & ProfileMode) {
          // Update render duration assuming we didn't error.
          stopProfilerTimerIfRunningAndRecordDelta(workInProgress, false);
        }
      } else {
        nextUnitOfWork = completeWork(
          current,
          workInProgress,
          nextRenderExpirationTime,
        );
      }
      if (__DEV__ && replayFailedUnitOfWorkWithInvokeGuardedCallback) {
        // We're out of completion phase so replaying is fine now.
        mayReplayFailedUnitOfWork = true;
      }
      stopWorkTimer(workInProgress);
      resetChildExpirationTime(workInProgress, nextRenderExpirationTime);
      if (__DEV__) {
        ReactCurrentFiber.resetCurrentFiber();
      }

      if (nextUnitOfWork !== null) {
        // Completing this fiber spawned new work. Work on that next.
        return nextUnitOfWork;
      }

      // 接下去這個(gè)個(gè)判斷,是要構(gòu)造一個(gè)所有 有 SideEffect 的節(jié)點(diǎn)的一個(gè)鏈狀的結(jié)構(gòu)
      // 這個(gè)鏈狀結(jié)構(gòu)最終是用于 commitWork 的時(shí)候用來(lái)進(jìn)行對(duì)這些有 SideEffect的節(jié)點(diǎn)進(jìn)行 commit 的一個(gè)操作,
      // 這邊它的一個(gè)判斷條件,returnFiber不等于null,并且returnFiber它不是一個(gè) Incomplete 的一個(gè)節(jié)點(diǎn)
      // 因?yàn)閷?duì)于一個(gè) Incomplete 的節(jié)點(diǎn),它唯一可以具有的一個(gè)SideEffect,就是這個(gè)節(jié)點(diǎn)已經(jīng)被捕獲了
      // 因?yàn)閷?duì)于有 Incomplete 錯(cuò)誤的節(jié)點(diǎn)是不會(huì)渲染正常的子節(jié)點(diǎn)的
      if (
        returnFiber !== null &&
        // Do not append effects to parents if a sibling failed to complete
        (returnFiber.effectTag & Incomplete) === NoEffect
      ) {
        // Append all the effects of the subtree and this fiber onto the effect
        // list of the parent. The completion order of the children affects the
        // side-effect order.
        // 對(duì)于正常的一個(gè)情況, 首先要判斷一下 returnFiber.firstEffect 是否等于 null
        // 符合判斷就代表 現(xiàn)在這個(gè) returnFiber 上還沒(méi)有記錄任何它的子節(jié)點(diǎn)的有副作用的子節(jié)點(diǎn)
        // 這個(gè)時(shí)候, 直接把當(dāng)前節(jié)點(diǎn)的firstEffect賦值給 returnFiber.firstEffect
        // 因?yàn)樗笆菦](méi)有任何一個(gè)的嘛,我們這邊真正要做的是把當(dāng)前節(jié)點(diǎn)的 firstEffect 到 lastEffect的一個(gè)鏈條
        // 這個(gè)單項(xiàng)鏈表,給它掛載到它的父節(jié)點(diǎn)的同樣的一個(gè) firstEffect到lastEffect的單項(xiàng)鏈表的最后
        if (returnFiber.firstEffect === null) {
          returnFiber.firstEffect = workInProgress.firstEffect;
        }
        // 就是下面這一段判斷,就是來(lái)做這個(gè)事情的, 如果returnFiber.lastEffect不等于null,那說(shuō)明它已經(jīng)有了
        // 那么對(duì)于returnFiber上面有記錄過(guò)別的 SideEffect 的節(jié)點(diǎn)之后
        // 我們當(dāng)前節(jié)點(diǎn)是掛載到整個(gè) SideEffect 鏈的最后,就是下面這樣,就是把它連到最后的上面
        if (workInProgress.lastEffect !== null) {
          if (returnFiber.lastEffect !== null) {
            returnFiber.lastEffect.nextEffect = workInProgress.firstEffect;
          }
          // 還要操作如下,因?yàn)樗倪@個(gè)(returnFiber.lastEffect)指針目前還指向它原來(lái)的那個(gè)lastEffect
          // 在 賦值 nextEffect之后,它的最后一個(gè)就是這個(gè)鏈的最后一個(gè)已經(jīng)變成 workInProgress.lastEffect,所以這邊要執(zhí)行這么一個(gè)操作
          // 當(dāng)然這個(gè)條件是要建立在 workInProgress.lastEffect 是有值的情況
          // 這是把它們各自的firsteffect到lasteffect,這個(gè)鏈給它進(jìn)行一個(gè)串聯(lián)的過(guò)程
          returnFiber.lastEffect = workInProgress.lastEffect;
        }

        // If this fiber had side-effects, we append it AFTER the children's
        // side-effects. We can perform certain side-effects earlier if
        // needed, by doing multiple passes over the effect list. We don't want
        // to schedule our own side-effect on our own list because if end up
        // reusing children we'll schedule this effect onto itself since we're
        // at the end.
        // 對(duì)于returnFiber來(lái)說(shuō),當(dāng)前這個(gè)節(jié)點(diǎn)也可能是有副作用的,那么這邊就接下去就會(huì)做這個(gè)操作
        // 如果當(dāng)前節(jié)點(diǎn)的 effectTag  > PerformedWork 的,因?yàn)?PerformedWork 是一個(gè)給 DEVTool 用的一個(gè) sideEffect
        // 對(duì)于真正的react更新是沒(méi)有任何意義的, 所以如果它僅僅只有 PerformedWork ,它就不是一個(gè)有效的 SideEffect 的節(jié)點(diǎn)
        const effectTag = workInProgress.effectTag;
        // Skip both NoWork and PerformedWork tags when creating the effect list.
        // PerformedWork effect is read by React DevTools but shouldn't be committed.
        if (effectTag > PerformedWork) {
          // 如果它有 SideEffect,就把當(dāng)前節(jié)點(diǎn)作為父節(jié)點(diǎn)的 SideEffect 鏈的最后一個(gè)給它掛載上去
          // 或者如果是當(dāng)前父節(jié)點(diǎn)沒(méi)有任何記錄的 SideEffect,它就是第一個(gè)
          if (returnFiber.lastEffect !== null) {
            returnFiber.lastEffect.nextEffect = workInProgress;
          } else {
            returnFiber.firstEffect = workInProgress;
          }
          returnFiber.lastEffect = workInProgress;
        }
      }

      if (__DEV__ && ReactFiberInstrumentation.debugTool) {
        ReactFiberInstrumentation.debugTool.onCompleteWork(workInProgress);
      }

      if (siblingFiber !== null) {
        // If there is more work to do in this returnFiber, do that next.
        return siblingFiber;
      } else if (returnFiber !== null) {
        // If there's no more work in this returnFiber. Complete the returnFiber.
        workInProgress = returnFiber;
        continue;
      } else {
        // We've reached the root.
        return null;
      }
    } else {
      if (enableProfilerTimer && workInProgress.mode & ProfileMode) {
        // Record the render duration for the fiber that errored.
        stopProfilerTimerIfRunningAndRecordDelta(workInProgress, false);

        // Include the time spent working on failed children before continuing.
        let actualDuration = workInProgress.actualDuration;
        let child = workInProgress.child;
        while (child !== null) {
          actualDuration += child.actualDuration;
          child = child.sibling;
        }
        workInProgress.actualDuration = actualDuration;
      }

      // This fiber did not complete because something threw. Pop values off
      // the stack without entering the complete phase. If this is a boundary,
      // capture values if possible.
      const next = unwindWork(workInProgress, nextRenderExpirationTime);
      // Because this fiber did not complete, don't reset its expiration time.
      if (workInProgress.effectTag & DidCapture) {
        // Restarting an error boundary
        stopFailedWorkTimer(workInProgress);
      } else {
        stopWorkTimer(workInProgress);
      }

      if (__DEV__) {
        ReactCurrentFiber.resetCurrentFiber();
      }

      if (next !== null) {
        stopWorkTimer(workInProgress);
        if (__DEV__ && ReactFiberInstrumentation.debugTool) {
          ReactFiberInstrumentation.debugTool.onCompleteWork(workInProgress);
        }

        // If completing this work spawned new work, do that next. We'll come
        // back here again.
        // Since we're restarting, remove anything that is not a host effect
        // from the effect tag.
        next.effectTag &= HostEffectMask;
        return next;
      }

      if (returnFiber !== null) {
        // Mark the parent fiber as incomplete and clear its effect list.
        returnFiber.firstEffect = returnFiber.lastEffect = null;
        returnFiber.effectTag |= Incomplete;
      }

      if (__DEV__ && ReactFiberInstrumentation.debugTool) {
        ReactFiberInstrumentation.debugTool.onCompleteWork(workInProgress);
      }

      // 存在 sibling 節(jié)點(diǎn), 注意這邊是return,也就是跳出while循環(huán)
      if (siblingFiber !== null) {
        // If there is more work to do in this returnFiber, do that next.
        return siblingFiber;
      } else if (returnFiber !== null) {
        // If there's no more work in this returnFiber. Complete the returnFiber.
        // 如果returnFiber不等于null,那么 workInProgress = returnFiber
        // 比如對(duì)于最上面示例圖的 input節(jié)點(diǎn),它的 completeUnitOfWork
        // 如果它沒(méi)有兄弟節(jié)點(diǎn),那么它就繼續(xù)執(zhí)行Input的 completeUnitOfWork
        // 這個(gè)循環(huán)是在 completeUnitOfWork 內(nèi)部進(jìn)行的一個(gè)過(guò)程
        workInProgress = returnFiber;
        continue;
      } else {
        // 如果 returnFiber 也等于null,那么它直接 return null
        // 說(shuō)明已經(jīng)到達(dá)頂點(diǎn)了,就到達(dá) RootFiber 了,我們的更新過(guò)程呢已經(jīng)完成了
        // 只需要在接下去 commitRoot 就可以了
        return null;
      }
    }
  }

  // Without this explicit null return Flow complains of invalid return type
  // TODO Remove the above while(true) loop
  // eslint-disable-next-line no-unreachable
  return null;
}
  • 對(duì)于這個(gè)while循環(huán)里面,其實(shí)它的所有代碼都是在if else里面的
  • if else它們最大的一個(gè)區(qū)別是什么呢?
    • 在if里面我們可以看這邊這個(gè)特殊情況
    • 就是 nextUnitOfWork 等于 completeWork,然后傳入一些屬性
    • 對(duì)于 else 的情況,這邊調(diào)用了 next = unwindWork
    • 這就是對(duì)于一個(gè)是否有拋出過(guò)錯(cuò)誤的一個(gè)節(jié)點(diǎn),執(zhí)行的一個(gè)不同的操作
  • completeWork,是沒(méi)有任何一個(gè)錯(cuò)誤的一個(gè)節(jié)點(diǎn),它的一個(gè)complete的一個(gè)過(guò)程
    • 調(diào)用了completework,就是把這個(gè)節(jié)點(diǎn)的更新給它完成了
  • unwindWork 是有節(jié)點(diǎn)的錯(cuò)誤,被捕獲,如何去處理的一個(gè)過(guò)程
  • 在上面的 SideEffect 到底有什么意義呢?
  • 比如有如下場(chǎng)景:
    • 有個(gè)列表上有值為 1, 2, 3的三個(gè)節(jié)點(diǎn)
    • 點(diǎn)擊 button 按鈕,會(huì)讓上面三個(gè)節(jié)點(diǎn)內(nèi)的值乘上自身
    • 對(duì)于2,3是4和9,對(duì)于1還是1
    • 這里 1 是一個(gè)沒(méi)有變化的過(guò)程
    • 按照正常邏輯來(lái)說(shuō),只需要更新原本值是 2 和 3 的節(jié)點(diǎn)
    • 實(shí)際上 react 里面也是這樣做的
  • 參考demo如下
    import React, { Component } from 'react'
    import './App.css'
    
    class List extends Component {
      state = {
        a: 1,
        b: 2,
        c: 3,
      }
    
      handleClick = () => {
        this.setState(oldState => {
          const { a, b, c } = oldState
          return {
            a: a * a,
            b: b * b,
            c: c * c,
          }
        })
      }
    
      render() {
        const { a, b, c } = this.state
        return [
          <span key="a">{a}</span>,
          <span key="b">{b}</span>,
          <span key="c">{c}</span>,
          <button key="button" onClick={this.handleClick}>
            click me
          </button>,
        ]
      }
    }
    
    class Input extends Component {
      state = {
        name: 'wang',
      }
    
      handleChange = e => {
        // 這里如果使用方法設(shè)置`state`
        // 那么需要現(xiàn)在外面讀取`e.target.value`
        // 因?yàn)樵赗eact走完整個(gè)事件之后會(huì)重置event對(duì)象
        // 以復(fù)用event對(duì)象,如果等到方法被調(diào)用的時(shí)候再讀取`e.target.value`
        // 那時(shí)`e.target`是`null`
        this.setState({
          name: e.target.value,
        })
      }
    
      render() {
        return (
          <input
            type="text"
            style={{ color: 'red' }}
            onChange={this.handleChange}
            value={this.state.name}
          />
        )
      }
    }
    
    class App extends Component {
      render() {
        return (
          <div className="main">
            <Input />
            <List />
          </div>
        )
      }
    }
    
    export default App
    
  • 這個(gè)時(shí)候我們?cè)?瀏覽器中針對(duì) react-dom.development.js 在 commitRoot 上面打了一個(gè)斷點(diǎn)
  • 這個(gè)斷點(diǎn)是看最終要進(jìn)行commit的時(shí)候,它是如何去獲取哪幾個(gè)節(jié)點(diǎn)是要更新的
  • 也就是我們的 SideEffect 鏈?zhǔn)窃趺礃拥囊粋€(gè)形式, 點(diǎn)一下,斷點(diǎn)被捕獲,捕獲了之后
  • 可以看到它接收的兩個(gè)參數(shù),一個(gè)是root,一個(gè)是finishedWork,那么它們其實(shí)是一個(gè)對(duì)應(yīng)的關(guān)系
    • root.current.alternate 就等于 finishedWork
    • 因?yàn)?finishedWork 對(duì)應(yīng)的是 workInProgress
    • finishedWork 上面就會(huì)記錄我們當(dāng)前這一次更新,它所有需要去執(zhí)行的,就是要更新到dom上面的一些內(nèi)容
      • 它的記錄就在 firstEffect 到 lastEffect 的一個(gè)鏈上
      • 它這邊 firstEffect 記錄的 elementType 是一個(gè) span
      • 所謂 effect 它最終是一個(gè)fiber節(jié)點(diǎn),就是告訴我們哪一個(gè)fiber節(jié)點(diǎn)需要更新
      • 對(duì)于我們這邊第二個(gè)span,它的值是要被更新成4的
      • 所以可以看到它的 firstEffect 的第一個(gè)節(jié)點(diǎn)是 span
      • 它的 effectTag是 4,然后它的 updateQueen 的屬性里面是一個(gè)數(shù)組 updateQueue: ["children", "4"]
      • 這個(gè)大致意思可以看出,我們要把這個(gè)span標(biāo)簽對(duì)應(yīng)的dom節(jié)點(diǎn), 它的children里面顯示的文字內(nèi)容,從2變成4
      • 還有就是下一個(gè)節(jié)點(diǎn), 也是一個(gè)span, 看 nextEffect, 它的 elementType 也是span
      • 它的 updateQueen 是 updateQueue: ["children", "9"],也就是原值為 3 的節(jié)點(diǎn)
  • 對(duì)于commitWork,只會(huì)關(guān)心 firstEffect 到 lastEffect 這個(gè)鏈條上面的對(duì)應(yīng)的 fiber 節(jié)點(diǎn)
    • 它只需要更新的就是這兩個(gè)dom節(jié)點(diǎn)的children的一個(gè)變化
  • 這就是react里面的 vdom,它以最小化的程度去更新 dom
  • 而不需要對(duì)其他的任何節(jié)點(diǎn)再進(jìn)行一個(gè)操作,以此來(lái)提升一個(gè)性能
  • 這個(gè)過(guò)程, 就是在 v16.6.3/packages/react-reconciler/src/ReactFiberScheduler.js#L1026 這個(gè) if 里面被實(shí)現(xiàn)的
  • 這個(gè) demo 示例對(duì)應(yīng)最最上面的流程圖來(lái)說(shuō)
    • 這邊點(diǎn)了 button之后,在這里創(chuàng)建了一個(gè)update
    • 就是這個(gè)List上面, 通過(guò)調(diào)用 this.setState 去創(chuàng)建了這個(gè) update
    • 最終是要從 RootFiber 上面往下進(jìn)行一個(gè)更新,更新到 List 中的第一個(gè)span,以及后續(xù)的span
    • 然后發(fā)現(xiàn)只有第2和第3個(gè)span,它們的 children從 2 變成了 4,從 3 變成了 9
    • 它們兩個(gè)對(duì)應(yīng)的 SideEffect 是 Update 對(duì)應(yīng)值為4 (二進(jìn)制的表示形式, 參考 ReactSideEffectTags.js),它們要把內(nèi)容掛載到dom上面
    • 第一個(gè)span的內(nèi)容因?yàn)槭?乘以1,所以它沒(méi)有變化,就不需要執(zhí)行真正的更新
    • 首先執(zhí)行 completeUnitOfWork 的是 第一個(gè) span, 接下去它的 sibling
    • 因?yàn)榈谝粋€(gè) span 沒(méi)有 SideEffect,在這里,它的 returnFiber 就是 List
    • 當(dāng)前的 workInProgress 就是這第一個(gè) span,這個(gè)span沒(méi)有任何的 SideEffect
    • 這個(gè) List 自己也沒(méi)有 SideEffect,所以賦值List的時(shí)候,對(duì)List也是沒(méi)有任何的一個(gè)更新的, 如下
      if (returnFiber.firstEffect === null) {
        returnFiber.firstEffect = workInProgress.firstEffect;
      }
      
    • 這時(shí)候的 List 本身的 firstEffect 到 lastEffect是沒(méi)有任何內(nèi)容的,也就是null
    • 進(jìn)行到第2個(gè)span節(jié)點(diǎn),它自己有 SideEffect, 而它沒(méi)有子節(jié)點(diǎn)
    • 所以它本身上面的 firstEffect到lastEffect是沒(méi)有任何內(nèi)容的
    • 唯一需要操作的是要把它作為L(zhǎng)ist的第一個(gè) SideEffect 增加到這個(gè)鏈上面
    • 因?yàn)槭?Update, 值為 4 肯定是大于 Performedwork 值為 1的
      if (effectTag > PerformedWork) {
        if (returnFiber.lastEffect !== null) {
          returnFiber.lastEffect.nextEffect = workInProgress; // 情況1,標(biāo)記為 L1, 以便下面引用說(shuō)明
        } else {
          returnFiber.firstEffect = workInProgress; // 情況2, 標(biāo)記為 L2
        }
        returnFiber.lastEffect = workInProgress; // 通用設(shè)定3,標(biāo)記為 L3
      }
      
    • 它自身(workInProgress)要作為 returnFiber 的第一個(gè) SideEffect
    • 給它增加到 List 的firstEffect到lastEffect的鏈上
    • 所以returnFiber的 firstEffect 和 lastEffect,都等于這個(gè)Fiber對(duì)象(第二個(gè)span) workInProgress
    • 對(duì)應(yīng)的執(zhí)行語(yǔ)句是 L2L3
    • 當(dāng)?shù)?個(gè)span執(zhí)行complete完了,又要開(kāi)始它的sibling, 也就是第三個(gè)span
    • 第三個(gè)span也是有更新的,所以它也要增加到 List上面
    • 這個(gè)時(shí)候List上面已經(jīng)有變化,因?yàn)長(zhǎng)ist.lastEffect(姑且這樣表示)已經(jīng)不等于null了
    • 這個(gè)時(shí)候,執(zhí)行的是 else 的情況,即 L1
    • 因?yàn)樵谏弦粋€(gè)節(jié)點(diǎn)的complete過(guò)程當(dāng)中,已經(jīng)指定了 firstEffect 和 lastEffect 都為 workInProgress(第2個(gè)span)
    • 所以第二個(gè)span節(jié)點(diǎn)的 nextEffect 指向的是第三個(gè)span的節(jié)點(diǎn)
    • 它們更新完之后又要更新 button,因?yàn)樗鼪](méi)有任何的變化,所以 button 也沒(méi)有更新
    • 這個(gè)時(shí)候?qū)τ贚ist來(lái)說(shuō)它的 SideEffect 鏈?zhǔn)堑扔?span指向span的這么一個(gè)過(guò)程
    • List的firstEffect指向第二個(gè)span對(duì)應(yīng)的Fiber對(duì)象,lastEffect指向第三個(gè)span對(duì)應(yīng)的Fiber對(duì)象
    • List本身執(zhí)行 completeUnitOfWork 的時(shí)候,自己沒(méi)有任何的更新
    • 因?yàn)樗膕tate更新對(duì)于真正的dom操作,沒(méi)有任何的關(guān)系,而且它也沒(méi)有生命周期方法
    • 所以 List 后期沒(méi)有任何要做的事情了, 自己本身是沒(méi)有 SideEffect,只有子節(jié)點(diǎn)的 SideEffect
    • 在 List 執(zhí)行 completeUnitOfWork 的時(shí)候, 代碼中的 returnFiber 對(duì)應(yīng)是 div
    • 而 workInProgress 對(duì)應(yīng)的是 List, 而 List 要賦值它的 firstEffect 和 lastEffect
    • 它的鏈指向returnFiber, 就是要放到 returnFiber 它的鏈的最后面
    • returnFiber 這個(gè) div,它目前是肯定沒(méi)有任何的 SideEffect的, 所以它直接賦值成 workInProgress.firstEffect
    • 也就是說(shuō),對(duì)于 div 它的 firstEffect 跟 lastEffect 已經(jīng)變成 List 的 firstEffect 跟 lastEffect
    • 因?yàn)樯厦婀?jié)點(diǎn)都是沒(méi)有任何更新的, 所以一層一層往上之后, 在 RootFiber 上面記錄的
    • firstEffect 跟 lastEffect 也就變成了最開(kāi)始的兩個(gè)span
    • 所以這就是通過(guò) completeUnitOfWork,最終能夠賦值到 RootFiber 上面
    • 在它的 firstEffect 到 lastEffect,單項(xiàng)鏈表上面去記錄整個(gè)應(yīng)用當(dāng)中
    • 所有需要去更新的最終的dom節(jié)點(diǎn)以及組件的fiber對(duì)象的一個(gè)過(guò)程
    • 這里涉及到后期 commitRoot 的時(shí)候, 調(diào)用這個(gè)單項(xiàng)鏈表一個(gè)過(guò)程,這里先跳過(guò)
  • 對(duì)于 unwindWork 的一個(gè)過(guò)程, 也是相似的, 先跳過(guò) unwindWork 以及 completeWork
  • 目前只關(guān)心它的一個(gè)遍歷過(guò)程,跳過(guò)具體節(jié)點(diǎn)做的事情, 關(guān)注它的大致流程
  • 它真正幫我們?nèi)?shí)現(xiàn)了把所有需要更新的節(jié)點(diǎn)進(jìn)行一個(gè)串聯(lián)
  • 讓最終 commitRoot 的時(shí)候,能夠方便獲取到所有需要更新的節(jié)點(diǎn)的一個(gè)過(guò)程

到了這里,關(guān)于React16源碼: React中的completeUnitOfWork的源碼實(shí)現(xiàn)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來(lái)自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場(chǎng)。本站僅提供信息存儲(chǔ)空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請(qǐng)注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請(qǐng)點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • React16源碼: React中的performWork的源碼實(shí)現(xiàn)

    performWork 1 )概述 performWork 涉及到在調(diào)度完成,或者同步任務(wù)進(jìn)來(lái)之后整個(gè) root 節(jié)點(diǎn)鏈條如何更新 怎么更新一棵 Fiber 樹(shù),它的每一個(gè)節(jié)點(diǎn)是如何被遍歷到,以及如何進(jìn)行更新操作 A. 在執(zhí)行 performWork 時(shí)候,是否有 deadline 的區(qū)分 deadline 是通過(guò) reactschedule 它的一個(gè)時(shí)間片,更新

    2024年01月17日
    瀏覽(21)
  • React16源碼: React中的IndeterminateComponent的源碼實(shí)現(xiàn)

    IndeterminateComponent 1 )概述 這是一個(gè)比較特殊的component的類型, 就是還沒(méi)有被指定類型的component 在一個(gè)fibrer被創(chuàng)建的時(shí)候,它的tag可能會(huì)是 IndeterminateComponent 在 packages/react-reconciler/src/ReactFiber.js 中,有一個(gè)方法 createFiberFromTypeAndProps 中,一開(kāi)始就聲明了 在最終調(diào)用 createFibe

    2024年01月21日
    瀏覽(21)
  • React16源碼: React中的renderRoot的源碼實(shí)現(xiàn)

    React16源碼: React中的renderRoot的源碼實(shí)現(xiàn)

    renderRoot 1 )概述 renderRoot 是一個(gè)非常復(fù)雜的方法 這個(gè)方法里處理很多各種各樣的邏輯, 它主要的工作內(nèi)容是什么? A. 它調(diào)用 workLoop 進(jìn)行循環(huán)單元更新 遍歷整個(gè) Fiber Tree,把每一個(gè)組件或者 dom 節(jié)點(diǎn)對(duì)應(yīng)的 Fiber 節(jié)點(diǎn)拿出來(lái)單一的進(jìn)行更新,這是一個(gè)循環(huán)的操作 把整棵 Fiber T

    2024年01月17日
    瀏覽(23)
  • React16源碼: React中的updateClassComponent的源碼實(shí)現(xiàn)

    ClassComponent 的更新 1 ) 概述 在 react 中 class component,是一個(gè)非常重要的角色 它承擔(dān)了 react 中 更新整個(gè)應(yīng)用的API setState forceUpdate 在react當(dāng)中,只有更新了state之后,整個(gè)應(yīng)用才會(huì)重新進(jìn)行渲染 在 class component 中, 它的邏輯相對(duì)復(fù)雜 2 )源碼 在 packages/react-reconciler/src/ReactFiberB

    2024年01月21日
    瀏覽(22)
  • React16源碼: React中的setState和forceUpdate源碼實(shí)現(xiàn)

    setState 和 forceUpdate 1 ) 概述 通過(guò) class component 內(nèi)部的 setState ,以及 forceUpdate 去更新一個(gè)組件的過(guò)程 在react的應(yīng)用當(dāng)中,我們只有 ReactDOM.render setState ,以及 forceUpdate 這幾種種方式去更新react的應(yīng)用是合理的,其他沒(méi)有什么特別常用的方式去更新了 而且react官方推薦的也是用

    2024年01月25日
    瀏覽(22)
  • React16源碼: React中的HostComponent & HostText的源碼實(shí)現(xiàn)

    HostComponent HostText 1 )概述 HostComponent 就是我們dom原生的這些節(jié)點(diǎn), 如: div, span, p 標(biāo)簽這種 使用的是小寫(xiě)字母開(kāi)頭的這些節(jié)點(diǎn)一般都認(rèn)為它是一個(gè) HostComponent HostText ,它是單純的文本節(jié)點(diǎn) 主要關(guān)注它們的一個(gè)更新過(guò)程 2 )源碼 定位到 packages/react-reconciler/src/ReactFiberBeginWork.js 進(jìn)

    2024年01月24日
    瀏覽(19)
  • React16源碼: React中的reconcileChildIterator和reconcileChildrenArray的源碼實(shí)現(xiàn)

    reconcileChildIterator 和 reconcileChildrenArray 1 )概述 在react更新某一個(gè)節(jié)點(diǎn)的時(shí)候,要根據(jù)這個(gè)節(jié)點(diǎn),它的類型去獲取它的children 比如說(shuō)如果是 Function Component,它要調(diào)用這個(gè) component 計(jì)算出它的return的屬性 return的屬性可能是一個(gè)數(shù)組,可能是單個(gè)的 ReactElement,可能是 number, string

    2024年01月20日
    瀏覽(46)
  • React16源碼: React中的不同的expirationTime的源碼實(shí)現(xiàn)

    不同的 expirationTime 1 )概述 在React中不僅僅有異步任務(wù) 大部分情況下都是同步的任務(wù),所以會(huì)有不同 expirationTime 的存在 2 )種類 A. Sync 模式,優(yōu)先級(jí)最高 任務(wù)創(chuàng)建完成之后,立馬更新到真正的dom里面 是一個(gè)創(chuàng)建即更新的流程 B. Async 模式, 異步模式 會(huì)有一個(gè)調(diào)度 包含一系列

    2024年02月01日
    瀏覽(19)
  • React16源碼: React中的PortalComponent創(chuàng)建, 調(diào)和, 更新的源碼實(shí)現(xiàn)

    PortalComponent 1 )概述 React Portal之所以叫Portal,因?yàn)樽龅木褪呛汀皞魉烷T(mén)”一樣的事情 render到一個(gè)組件里面去,實(shí)際改變的是網(wǎng)頁(yè)上另一處的DOM結(jié)構(gòu) 主要關(guān)注 portal的創(chuàng)建, 調(diào)和, 更新過(guò)程 2 )源碼 定位到 packages/react-dom/src/client/ReactDOM.js#L576 這里調(diào)用的是 ReactPortal.createPortal ,

    2024年01月21日
    瀏覽(25)
  • React16源碼: React中的update和updateQueue的源碼實(shí)現(xiàn)

    React中的update和updateQueue 1 )概述 在 ReactDOM.render 過(guò)程中,還需要?jiǎng)?chuàng)建一個(gè) update 對(duì)象 update 用于記錄組件狀態(tài)的改變的一個(gè)對(duì)象,它存放于Fiber對(duì)象的 updateQueue 中 updateQueue ,它是一個(gè)單向鏈表的結(jié)構(gòu),一次整體的更新過(guò)程當(dāng)中 可能在這個(gè)queue里會(huì)存在多 Update 在這次更新的過(guò)

    2024年02月02日
    瀏覽(14)

覺(jué)得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請(qǐng)作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包