自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

React 源碼中最重要的部分,你知道有哪些嗎?

開(kāi)發(fā) 前端
BeginWork 與 CompleteWork 的執(zhí)行是 React 源碼中最重要的部分,理解他們的核心邏輯能有效幫助我們做好項(xiàng)目的性能優(yōu)化。因此在學(xué)習(xí)他們的過(guò)程中,應(yīng)該結(jié)合實(shí)踐去思考優(yōu)化策略。

無(wú)論是并發(fā)模式,還是同步模式,最終要生成新的 Fiber Tree,都是通過(guò)遍歷 workInProgress 的方式去執(zhí)行 performUnitOfWork。

// 并發(fā)模式
function workLoopConcurrent() {
  // Perform work until Scheduler asks us to yield
  while (workInProgress !== null && !shouldYield()) {
    performUnitOfWork(workInProgress);
  }
}
// 同步
function workLoopSync() {
  // Already timed out, so perform work without checking if we need to yield.
  while (workInProgress !== null) {
    performUnitOfWork(workInProgress);
  }
}

需要特別注意的是這里的 workInProgress 表示當(dāng)前正在執(zhí)行的 Fiber 節(jié)點(diǎn),他會(huì)在遞歸的過(guò)程中不斷改變指向,這里要結(jié)合我們之前章節(jié)中分享過(guò)的 Fiber Tree 的鏈表結(jié)構(gòu)來(lái)理解。

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

一、performUnitOfWork

該方法主要用于創(chuàng)建 Fiber Tree,是否理解 Fiber Tree 的構(gòu)建過(guò)程,跟我們是否能做好性能優(yōu)化有非常直接的關(guān)系,因此對(duì)我而言,這是 React 源碼中最重要的一個(gè)部分。

從他的第一行代碼我們就能知道,F(xiàn)iber Tree 的創(chuàng)建是依賴(lài)于雙緩存策略。上一輪構(gòu)建完成的 Fiber tree,在代碼中用 current 來(lái)表示。

正在構(gòu)建中的 Fiber tree,在代碼中用 workInProgress 來(lái)表示,并且他們之間同層節(jié)點(diǎn)都用 alternate 相互指向。

current.alternate = workInProgress;
workInProgress.alternate = current;

workInProgress 會(huì)基于 current 構(gòu)建。

function performUnitOfWork(unitOfWork: Fiber): void {
  const current = unitOfWork.alternate;
  ...

整體的思路是從 current[rootFiber] 樹(shù)往下執(zhí)行深度遍歷,在遍歷的過(guò)程中,會(huì)根據(jù) key、props、context、state 等條件進(jìn)行判斷,判斷結(jié)果如果發(fā)現(xiàn)節(jié)點(diǎn)沒(méi)有發(fā)生變化,那么就復(fù)用 current 的節(jié)點(diǎn),如果發(fā)生了變化,則重新創(chuàng)建 Fiber 節(jié)點(diǎn),并標(biāo)記需要修改的類(lèi)型,用于傳遞給 commitRoot。

二、beginWork

每一個(gè)被遍歷到的 Fiber 節(jié)點(diǎn),會(huì)執(zhí)行 beginWork 方法。

let next;
if (enableProfilerTimer && (unitOfWork.mode & ProfileMode) !== NoMode) {
  startProfilerTimer(unitOfWork);
  next = beginWork(current, unitOfWork, subtreeRenderLanes);
  stopProfilerTimerIfRunningAndRecordDelta(unitOfWork, true);
} else {
  next = beginWork(current, unitOfWork, subtreeRenderLanes);
}

該方法根據(jù)傳入的 Fiber 節(jié)點(diǎn)創(chuàng)建子節(jié)點(diǎn),并將這兩個(gè)節(jié)點(diǎn)連接起來(lái)。

function beginWork(
  current: Fiber | null,
  workInProgress: Fiber,
  renderLanes: Lanes,
): Fiber | null {...}

React 在 ReactFiberBeginWork.new.js 模塊中維護(hù)了一個(gè)全局的 didReceiveUpdate 變量,來(lái)表示當(dāng)前節(jié)點(diǎn)是否需要更新。

let didReceiveUpdate: boolean = false;

在 beginWork 的執(zhí)行過(guò)程中,會(huì)經(jīng)歷一些判斷來(lái)確認(rèn) didReceiveUpdate 的值,從而判斷該 Fiber 節(jié)點(diǎn)是否需要重新執(zhí)行。

if (current !== null) {
    const oldProps = current.memoizedProps;
    const newProps = workInProgress.pendingProps;

    if (
      oldProps !== newProps ||
      hasLegacyContextChanged() ||
      // Force a re-render if the implementation changed due to hot reload:
      (__DEV__ ? workInProgress.type !== current.type : false)
    ) {
      // If props or context changed, mark the fiber as having performed work.
      // This may be unset if the props are determined to be equal later (memo).
      didReceiveUpdate = true;
    } else {

這里比較的是 props 和 context 是否發(fā)生了變化。當(dāng)他們其中一個(gè)變化時(shí),則將 didReceiveUpdate 設(shè)置為 true。

這里的 hasLegacyContextChanged()  兼容的是舊版本 的 context,新版本的 context 是否發(fā)生變化會(huì)反應(yīng)到 pending update 中,也就是使用下面的 checkScheduledUpdateOrContext 來(lái)查看是否有更新的調(diào)度任務(wù)。

當(dāng) props 和 context 都沒(méi)有發(fā)生變化,并且也不存在對(duì)應(yīng)的調(diào)度任務(wù)時(shí),將其設(shè)置為 false。

如果有 state/context 發(fā)生變化,則會(huì)存在調(diào)度任務(wù)。

} else {
  // Neither props nor legacy context changes. Check if there's a pending
  // update or context change.
  const hasScheduledUpdateOrContext = checkScheduledUpdateOrContext(
    current,
    renderLanes,
  );
  if (
    !hasScheduledUpdateOrContext &&
    // If this is the second pass of an error or suspense boundary, there
    // may not be work scheduled on `current`, so we check for this flag.
    (workInProgress.flags & DidCapture) === NoFlags
  ) {
    // No pending updates or context. Bail out now.
    didReceiveUpdate = false;
    return attemptEarlyBailoutIfNoScheduledUpdate(
      current,
      workInProgress,
      renderLanes,
    );
  }

這里有一個(gè)很關(guān)鍵的點(diǎn),就在于當(dāng)方法進(jìn)入到 attemptEarlyBailoutIfNoScheduledUpdate 去判斷子節(jié)點(diǎn)是否可以 bailout 時(shí),他并沒(méi)有比較子節(jié)點(diǎn)的 props。

核心的邏輯在 bailoutOnAlreadyFinishedWork 中。

{
  ...
  // 判斷子節(jié)點(diǎn)是否有 pending 任務(wù)要做
  if (!includesSomeLane(renderLanes, workInProgress.childLanes)) {
    // The children don't have any work either. We can skip them.
    return null
  }

  // This fiber doesn't have work, but its subtree does. Clone the child
  // fibers and continue.
  cloneChildFibers(current, workInProgress);
  return workInProgress.child;
}

所以這里有一個(gè)很重要的思考就是為什么判斷子節(jié)點(diǎn)是否發(fā)生變化時(shí),并沒(méi)有去比較 props,這是性能優(yōu)化策略的關(guān)鍵一步,結(jié)合我們之前講的性能優(yōu)化策略去理解,你就能知道答案。

回到 beginWork, 后續(xù)的邏輯會(huì)根據(jù)不同的 tag,創(chuàng)建不同類(lèi)型的 Fiber 節(jié)點(diǎn)。

switch (workInProgress.tag) {
  case IndeterminateComponent: {
    return mountIndeterminateComponent(
      current,
      workInProgress,
      workInProgress.type,
      renderLanes,
    );
  }
  case LazyComponent: {
    const elementType = workInProgress.elementType;
    return mountLazyComponent(
      current,
      workInProgress,
      elementType,
      renderLanes,
    );
  }
  case FunctionComponent: {
    const Component = workInProgress.type;
    const unresolvedProps = workInProgress.pendingProps;
    const resolvedProps =
      workInProgress.elementType === Component
        ? unresolvedProps
        : resolveDefaultProps(Component, unresolvedProps);
    return updateFunctionComponent(
      current,
      workInProgress,
      Component,
      resolvedProps,
      renderLanes,
    );
  }
  case ClassComponent: {
    const Component = workInProgress.type;
    const unresolvedProps = workInProgress.pendingProps;
    const resolvedProps =
      workInProgress.elementType === Component
        ? unresolvedProps
        : resolveDefaultProps(Component, unresolvedProps);
    return updateClassComponent(
      current,
      workInProgress,
      Component,
      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: {
    const type = workInProgress.type;
    const unresolvedProps = workInProgress.pendingProps;
    const resolvedProps =
      workInProgress.elementType === type
        ? unresolvedProps
        : resolveDefaultProps(type, unresolvedProps);
    return updateForwardRef(
      current,
      workInProgress,
      type,
      resolvedProps,
      renderLanes,
    );
  }
  
  // ...
  
  case MemoComponent: {
    const type = workInProgress.type;
    const unresolvedProps = workInProgress.pendingProps;
    // Resolve outer props first, then resolve inner props.
    let resolvedProps = resolveDefaultProps(type, unresolvedProps);
    if (__DEV__) {
      if (workInProgress.type !== workInProgress.elementType) {
        const outerPropTypes = type.propTypes;
        if (outerPropTypes) {
          checkPropTypes(
            outerPropTypes,
            resolvedProps, // Resolved for outer only
            'prop',
            getComponentNameFromType(type),
          );
        }
      }
    }
    resolvedProps = resolveDefaultProps(type.type, resolvedProps);
    return updateMemoComponent(
      current,
      workInProgress,
      type,
      resolvedProps,
      renderLanes,
    );
  }
}
// ... 其他類(lèi)型

我們重點(diǎn)關(guān)注 updateFunctionComponent 的執(zhí)行邏輯,可以發(fā)現(xiàn),當(dāng) didReceiveUpdate 為 false 時(shí),會(huì)執(zhí)行 bailout 跳過(guò)創(chuàng)建過(guò)程。

if (current !== null && !didReceiveUpdate) {
  bailoutHooks(current, workInProgress, renderLanes);
  return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes);
}

如果無(wú)法 bailout,則最后執(zhí)行 reconcileChildren 創(chuàng)建新的子節(jié)點(diǎn)。

reconcileChildren(current, workInProgress, nextChildren, renderLanes);
  return workInProgress.child;

另外我們還應(yīng)該關(guān)注 updateMemoComponent 中的邏輯。該邏輯通過(guò)淺比較函數(shù) shallowEqual 來(lái)比較更新前后兩個(gè) props 的差異。當(dāng)比較結(jié)果為 true 時(shí),也是調(diào)用 bailout 跳過(guò)創(chuàng)建。

而不是沿用 didReceiveUpdate 的結(jié)果。

if (!hasScheduledUpdateOrContext) {
  // This will be the props with resolved defaultProps,
  // unlike current.memoizedProps which will be the unresolved ones.
  const prevProps = currentChild.memoizedProps;
  // Default to shallow comparison
  let compare = Component.compare;
  compare = compare !== null ? compare : shallowEqual;
  if (compare(prevProps, nextProps) && current.ref === workInProgress.ref) {
    return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes);
  }
}

二、completeWork

在 performUnitOfWork 執(zhí)行過(guò)程中,當(dāng)發(fā)現(xiàn)當(dāng)前節(jié)點(diǎn)已經(jīng)沒(méi)有子節(jié)點(diǎn)了,就會(huì)調(diào)用 completeUnitOfWork 方法。

if (next === null) {
  // If this doesn't spawn new work, complete the current work.
  completeUnitOfWork(unitOfWork);

該方法主要用于執(zhí)行 completeWork。completeWork 主要的作用是用于創(chuàng)建與 Fiber 節(jié)點(diǎn)對(duì)應(yīng)的 DOM 節(jié)點(diǎn)。

這里創(chuàng)建的 DOM 節(jié)點(diǎn)并沒(méi)有插入到 HTML 中,還存在于內(nèi)存里。

const instance = createInstance(
  type,
  newProps,
  rootContainerInstance,
  currentHostContext,
  workInProgress,
);

appendAllChildren(instance, workInProgress, false, false);

由于 completeWork 的執(zhí)行是從葉子節(jié)點(diǎn),往根節(jié)點(diǎn)執(zhí)行,因此,每次我們將新創(chuàng)建的節(jié)點(diǎn) append 到父節(jié)點(diǎn),執(zhí)行到最后 rootFiber 時(shí),一個(gè)完整的 DOM 樹(shù)就已經(jīng)構(gòu)建完成了。

completeWork 的執(zhí)行順序是一個(gè)回溯的過(guò)程。

當(dāng)然,F(xiàn)iber 節(jié)點(diǎn)與 DOM 節(jié)點(diǎn)之間,也會(huì)保持一一對(duì)應(yīng)的引用關(guān)系,因此在更新階段,我們能夠輕易的判斷和復(fù)用已經(jīng)存在的 DOM 節(jié)點(diǎn)從而避免重復(fù)創(chuàng)建。

四、遍歷順序

beginWork 和 completeWork 的執(zhí)行順序理解起來(lái)比較困難,為了便于理解,我們這里用一個(gè)圖示來(lái)表達(dá)。

例如有這樣一個(gè)結(jié)構(gòu)的節(jié)點(diǎn)。

<div id="root">
  <div className="1">
    <div className="1-1">1-1</div>
    <div className="1-2">1-2</div>
    <div className="1-3">
      <div className="1-3-1">1-3-1</div>
    </div>
  </div>
  <div className="2">2</div>
  <div className="3">3</div>
</div>

beginWork 的執(zhí)行是按照 Fiber 節(jié)點(diǎn)的鏈表深度遍歷執(zhí)行。

completeWork 則是當(dāng) fiber.next === null 時(shí)開(kāi)始執(zhí)行,他一個(gè)從葉子節(jié)點(diǎn)往根節(jié)點(diǎn)執(zhí)行的回溯過(guò)程。當(dāng)葉子節(jié)點(diǎn)被執(zhí)行過(guò)后,則對(duì)葉子節(jié)點(diǎn)的父節(jié)點(diǎn)執(zhí)行 completeWork。

下圖就是上面 demo 的執(zhí)行順序。

其中藍(lán)色圓代表對(duì)應(yīng)節(jié)點(diǎn)的 beginWork 執(zhí)行。黃色圓代表對(duì)應(yīng)節(jié)點(diǎn)的 completeWork 執(zhí)行。

五、總結(jié)

beginWork 與 completeWork 的執(zhí)行是 React 源碼中最重要的部分,理解他們的核心邏輯能有效幫助我們做好項(xiàng)目的性能優(yōu)化。因此在學(xué)習(xí)他們的過(guò)程中,應(yīng)該結(jié)合實(shí)踐去思考優(yōu)化策略。

不過(guò)性能優(yōu)化的方式在我們之前的章節(jié)中已經(jīng)詳細(xì)介紹過(guò),因此這里帶大家閱讀源碼更多的是做一個(gè)驗(yàn)證,去揭開(kāi)源碼的神秘面紗。

到這篇文章這里,React 原理的大多數(shù)重要邏輯我們?cè)谥车奈恼露家呀?jīng)給大家分享過(guò)了,其中包括同步更新邏輯,異步更新邏輯,任務(wù)優(yōu)先級(jí)隊(duì)列,任務(wù)調(diào)度,F(xiàn)iber 中的各種鏈表結(jié)構(gòu),各種比較方式的成本,包括本文介紹的 Fiber tree 的構(gòu)建過(guò)程,大家可以把這些零散的文章串起來(lái)總結(jié)一下,有能力的可以自己在閱讀源碼時(shí)結(jié)合我分享的內(nèi)容進(jìn)一步擴(kuò)展和完善。

閱讀源碼是一個(gè)高投入,低回報(bào)的過(guò)程,希望我的這些文章能有效幫助大家以更低的時(shí)間成本獲得更高的知識(shí)回報(bào)。

責(zé)任編輯:姜華 來(lái)源: 這波能反殺
相關(guān)推薦

2022-10-08 23:46:47

JavaScript對(duì)象開(kāi)發(fā)

2023-10-16 23:12:02

Redis數(shù)據(jù)結(jié)構(gòu)

2023-04-26 10:06:08

RocketMQ屬性Consumer

2021-03-11 07:26:52

垃圾回收器單線(xiàn)程

2010-04-16 14:51:05

網(wǎng)絡(luò)流量

2020-03-23 08:15:43

JavaScriptError對(duì)象函數(shù)

2022-08-02 06:55:35

移動(dòng)設(shè)備Android

2022-06-30 13:41:44

SQL 語(yǔ)句group by

2019-09-21 21:15:36

MapReduce大數(shù)據(jù)分布式

2023-11-29 07:29:28

ReactSolid

2019-04-30 08:25:35

2021-08-31 09:55:57

服務(wù)開(kāi)發(fā)K8S

2022-12-09 19:00:02

Vite兼容性BigInt

2020-11-04 17:35:39

網(wǎng)絡(luò)安全漏洞技術(shù)

2024-10-22 09:59:36

虛擬化容器化系統(tǒng)

2024-02-26 08:04:38

ReactReact.js場(chǎng)景

2010-09-14 10:14:15

2023-10-28 09:00:03

進(jìn)程系統(tǒng)服務(wù)

2015-06-09 05:45:12

iOSOS XWWDC

2019-02-23 15:00:43

點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)