React16源碼解析(五)-更新流程渲染階段1

React源碼解析系列文章歡迎閱讀:
React16源碼解析(一)- 圖解Fiber架構
React16源碼解析(二)-創建更新
React16源碼解析(三)-ExpirationTime
React16源碼解析(四)-Scheduler
React16源碼解析(五)-更新流程渲染階段1
React16源碼解析(六)-更新流程渲染階段2
React16源碼解析(七)-更新流程渲染階段3
React16源碼解析(八)-更新流程提交階段
正在更新中...

上篇文章講解到renderRoot & completeRoot。
React把組件的更新過程分爲了兩個階段:渲染階段(renderRoot)和提交階段(completeRoot)。
本文討論渲染階段。

從上篇文章末尾的renderRoot講起:

renderRoot

1、nextUnitOfWork = createWorkInProgress() 拷貝一份 fiber 節點,在 nextUnitOfWork 中修改,防止改變當前 fiberTree。nextUnitOfWork 是下一個要更新的節點。
2、進入workLoop。

// 開始渲染整顆樹,這個函數在異步模式下可能會被多次執行,因爲在異步模式下
// 可以打斷任務。打斷也就意味着每次都得回到 root 再開始從上往下循環
function renderRoot(
  root: FiberRoot,
  isYieldy: boolean,
  isExpired: boolean,
): void {
  isWorking = true;
  ReactCurrentOwner.currentDispatcher = Dispatcher;

  const expirationTime = root.nextExpirationTimeToWorkOn;

  // Check if we're starting from a fresh stack, or if we're resuming from
  // previously yielded work.
  if (
    expirationTime !== nextRenderExpirationTime ||
    root !== nextRoot ||
    nextUnitOfWork === null
  ) {
    // Reset the stack and start working from the root.
    resetStack();
    nextRoot = root;
    nextRenderExpirationTime = expirationTime;
    // 獲取下一個需要工作的單元
    nextUnitOfWork = createWorkInProgress(
      // FiberRoot 對應的 Rootfiber
      nextRoot.current,
      null,
      nextRenderExpirationTime,
    );
    root.pendingCommitExpirationTime = NoWork;
    // ......
  }
  // ......
  do {
    try {
      // 循環更新節點
      workLoop(isYieldy);
    } catch (thrownValue) {
      // 遇到某種錯誤
    break;
  } while (true);

  // 這裏有一大堆的錯誤處理
  
  // Ready to commit.
  onComplete(root, rootWorkInProgress, expirationTime);
}

workLoop

循環單元更新,對整顆 fiberTree 都遍歷一遍。

還記得之前傳入進來的isYieldy的麼,如果爲false,不可中斷,不斷的更新下一個節點任務(performUnitOfWork(nextUnitOfWork)),知道整棵樹更新完畢。如果可以中斷,通過shouldYield()判斷當前幀是否還有時間更新,有時間就更新,沒有時間了就不更了。

注:在workLoop 跟新後會有各種錯誤處理。

function workLoop(isYieldy) {
  // 對 nextUnitOfWork 循環進行判斷,直到沒有 nextUnitOfWork
  if (!isYieldy) {
    // 不可中斷
    // Flush work without yielding
    while (nextUnitOfWork !== null) {
      // 一開始進來 nextUnitOfWork 是 root,每次執行 performUnitOfWork 後
      // 都會生成下一個工作單元
      nextUnitOfWork = performUnitOfWork(nextUnitOfWork);
    }
  } else {
    // 可中斷
    // Flush asynchronous work until the deadline runs out of time.
    while (nextUnitOfWork !== null && !shouldYield()) {
      nextUnitOfWork = performUnitOfWork(nextUnitOfWork);
    }
  }
}

performUnitOfWork

1、調用beginWork()更新當前任務節點
2、如果當前fiber樹已經更新到葉子節點了,則調用completeUnitOfWork更新。

// 開始組件更新
function performUnitOfWork(workInProgress: Fiber): Fiber | null {
  // 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.
  // 獲得 fiber 的替身,調和這一階段都是在替身上完成的
  // 然後直接看 beginWork
  const current = workInProgress.alternate;

  // ......
  let next;

  // .....
  // 開始工作
  next = beginWork(current, workInProgress, nextRenderExpirationTime);
  workInProgress.memoizedProps = workInProgress.pendingProps;

  // ......

  // 當前fiber樹已經更新到葉子節點了
  if (next === null) {
    // If this doesn't spawn new work, complete the current work.
    next = completeUnitOfWork(workInProgress);
  }

  ReactCurrentOwner.current = null;

  return next;
}

beginWork

通過workInProgress.tag判斷當前是什麼類型的節點。
workInProgress.tag的所有類型定義在ReactWorkTags.js裏面

export const FunctionComponent = 0; //函數式組件
export const ClassComponent = 1; //class類組件
export const IndeterminateComponent = 2; // 並不確定是函數組件還是類組件
export const HostRoot = 3; // 組件樹根組件,可以嵌套
export const HostPortal = 4; // 子樹. Could be an entry point to a different renderer.
export const HostComponent = 5;// 標準組件,如地div, span等
export const HostText = 6;// 文本
export const Fragment = 7;// 片段
export const Mode = 8;
export const ContextConsumer = 9;
export const ContextProvider = 10;
export const ForwardRef = 11;
export const Profiler = 12;
export const SuspenseComponent = 13;
export const MemoComponent = 14;
export const SimpleMemoComponent = 15;
export const LazyComponent = 16;
export const IncompleteClassComponent = 17;

接下來我會依次講解下面這些組件的更新:

1、FunctionComponent(函數組件),return updateFunctionComponent(…)。
2、ClassComponent(class類組件) return updateClassComponent(…)
3、IndeterminateComponent(不確定是函數組件還是類組件),return mountIndeterminateComponent(…)
4、HostRoot(組件樹根組件),return updateHostRoot(…);
5、HostComponent(標準dom節點),return updateHostComponent(…);
6、HostText(文本),updateHostText(…);
7、HostPortal,updatePortalComponent(…)
8、ForwardRef,updateForwardRef(…)
9、Mode,updateMode(…)
10、MemoComponent,updateMemoComponent(….)

function beginWork(
  current: Fiber | null,
  workInProgress: Fiber,
  renderExpirationTime: ExpirationTime,
): Fiber | null {
  const updateExpirationTime = workInProgress.expirationTime;

  if (current !== null) {
    const oldProps = current.memoizedProps;
    const newProps = workInProgress.pendingProps;
    // 判斷 props 和 context 是否改變
    // 判斷當前 fiber 的優先級是否小於本次渲染的優先級,小於的話可以跳過
    if (
      oldProps === newProps &&
      !hasLegacyContextChanged() &&
      (updateExpirationTime === NoWork ||
        updateExpirationTime > renderExpirationTime)
    ) {
      // ......
      }
      // bailoutOnAlreadyFinishedWork 會判斷這個 fiber 的子樹是否需要更新,如果有需要更新會 clone 一份到 workInProgress.child 返回到 workLoop 的 nextUnitOfWork, 否則爲 null
      return bailoutOnAlreadyFinishedWork(
        current,
        workInProgress,
        renderExpirationTime,
      );
    }
  }

  // Before entering the begin phase, clear the expiration time.
  // 進行更新先把當前 fiber 的 expirationTime 設置爲 NoWork
  workInProgress.expirationTime = NoWork;
  // 根據 fiber 的 tag 類型進行更新
  switch (workInProgress.tag) {
    case IndeterminateComponent: ......
    case LazyComponent: ......
    case FunctionComponent: ......
    case ClassComponent: ......
    case HostRoot:......
    case HostComponent:......
    case HostText:......
    case SuspenseComponent:......
    case HostPortal:......
    case ForwardRef: ......
    case Fragment:......
    case Mode:......
    case Profiler:......
    case ContextProvider:......
    case ContextConsumer:......
    case MemoComponent: ......
    case SimpleMemoComponent: ......
    case IncompleteClassComponent:......
    default:
      invariant(
        false,
        'Unknown unit of work tag. This error is likely caused by a bug in ' +
          'React. Please file an issue.',
      );
  }
}

下篇文章繼續講解這些組件的更新。

任世界紛繁複雜,仍舊保持可愛。
我是小柚子小仙女。文章如有不妥,歡迎指正~

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章