溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

useEffect返回函數(shù)執(zhí)行過(guò)程是什么

發(fā)布時(shí)間:2023-04-17 10:46:02 來(lái)源:億速云 閱讀:126 作者:iii 欄目:開(kāi)發(fā)技術(shù)

本文小編為大家詳細(xì)介紹“useEffect返回函數(shù)執(zhí)行過(guò)程是什么”,內(nèi)容詳細(xì),步驟清晰,細(xì)節(jié)處理妥當(dāng),希望這篇“useEffect返回函數(shù)執(zhí)行過(guò)程是什么”文章能幫助大家解決疑惑,下面跟著小編的思路慢慢深入,一起來(lái)學(xué)習(xí)新知識(shí)吧。

下面是源碼簡(jiǎn)化:

function recursivelyTraversePassiveUnmountEffects(parentFiber: Fiber): void {
  const deletions = parentFiber.deletions;
  if ((parentFiber.flags & ChildDeletion) !== NoFlags) {
    if (deletions !== null) {
      for (let i = 0; i < deletions.length; i++) {
        const childToDelete = deletions[i];
        nextEffect = childToDelete;
        commitPassiveUnmountEffectsInsideOfDeletedTree_begin(
          childToDelete,
          parentFiber
        );
      }
    }
  }
}
function commitPassiveUnmountEffectsInsideOfDeletedTree_begin(
  deletedSubtreeRoot: Fiber,
  nearestMountedAncestor: Fiber | null
) {
  while (nextEffect !== null) {
    const fiber = nextEffect;
    // 執(zhí)行 passive effects 返回的函數(shù)
    commitPassiveUnmountInsideDeletedTreeOnFiber(fiber, nearestMountedAncestor);

    const child = fiber.child;
    if (child !== null) {
      child.return = fiber;
      nextEffect = child;
    } else {
      commitPassiveUnmountEffectsInsideOfDeletedTree_complete(
        deletedSubtreeRoot
      );
    }
  }
}

function commitPassiveUnmountEffectsInsideOfDeletedTree_complete(
  deletedSubtreeRoot
) {
  while (nextEffect !== null) {
    const fiber = nextEffect;
    const sibling = fiber.sibling;
    const returnFiber = fiber.return;

    if (fiber === deletedSubtreeRoot) {
      nextEffect = null;
      return;
    }

    if (sibling !== null) {
      sibling.return = returnFiber;
      nextEffect = sibling;
      return;
    }

    nextEffect = returnFiber;
  }
}

deletions

在正式開(kāi)始之前,我們要了解一個(gè) fiber 的屬性:deletions

這個(gè)屬性存放的是當(dāng)前節(jié)點(diǎn)中被刪除的 fiber,這個(gè)數(shù)組是在 commit 階段被賦值的

如果有被刪除的節(jié)點(diǎn),這個(gè)屬性值是一個(gè)數(shù)組,如果沒(méi)有被刪除的節(jié)點(diǎn),這個(gè)屬性值是 null

const A = () => {
  useEffect(() => {
    return () => {
      console.log("A unmount");
    };
  }, []);
  return <div>文本A</div>;
};
const B = () => {
  useEffect(() => {
    return () => {
      console.log("B unmount");
    };
  }, []);
  return <div>文本B</div>;
};

如果 App 組件這樣寫(xiě),那么 deletions 的值是 [FiberNode, FiberNode]

const App(){
  const [count, setCount] = useState(0)

  return <div>
    {count % 2 === 0 && <A />}
    {count % 2 === 0 && <B />}
    <div onClick={()=> setCount(count+1)}>+1</div>
  </div>
}

如果 App 組件這樣寫(xiě),那么 deletions 的值是 [FiberNode]

const App(){
  const [count, setCount] = useState(0)

  return <div>
    {count % 2 === 0 && <><A /><B /></>}
    <div onClick={()=> setCount(count+1)}>+1</div>
  </div>
}

對(duì)于第二種情況,react 會(huì)把 A 組件和 B 組件作為一個(gè)整體,所以 deletions 的值是 [FiberNode]

處理當(dāng)前節(jié)點(diǎn)的 deletions

react 在遍歷 fiber tree 時(shí),會(huì)先處理當(dāng)前的 fiberdeletions,等處理完之后再遍歷下一個(gè) fiber

現(xiàn)在我們已經(jīng)知道 deletions 中保存的是當(dāng)前 fiber 下被刪除的子節(jié)點(diǎn)

這時(shí) react 會(huì)遍歷 deletions 數(shù)組,然后執(zhí)行每個(gè) fiberpassive effect 返回的函數(shù)

但是有個(gè)問(wèn)題,如果 deletions 中的 fiber 有子節(jié)點(diǎn),那么這些子節(jié)點(diǎn)也會(huì)被刪除,這時(shí) react 會(huì)怎么處理呢?

這里分兩種情況來(lái)討論:

  • 刪除的 fiber 沒(méi)有子節(jié)點(diǎn):<div>{xxxx && <A />}</div>

  • 刪除的 fiber 有子節(jié)點(diǎn):<div>{xxxx && <><A /><B /></>}</div> -->

刪除的 fiber 沒(méi)有子節(jié)點(diǎn):<div>{xxxx && <A />}</div>

這種情況比較好理解

當(dāng)遍歷到 div 時(shí),因?yàn)?<A/> 節(jié)點(diǎn)會(huì)被卸載,所以在 divdeletions 保存了一個(gè) <A/>fiber

遍歷 deletions 數(shù)組,執(zhí)行 <A/>passive effect 返回的函數(shù)

如下圖所示:

useEffect返回函數(shù)執(zhí)行過(guò)程是什么

刪除的 fiber 有子節(jié)點(diǎn):<div>{xxxx && <><A /><B /></>}</div>

這種情況就比較復(fù)雜了

當(dāng)遍歷到 div 時(shí),<></> 節(jié)點(diǎn)會(huì)被卸載,所以在 divdeletions 保存了一個(gè) <></>fiber

遍歷 deletions 數(shù)組,執(zhí)行 fiberpassive effect 返回的函數(shù),對(duì)于 <></> 來(lái)說(shuō)是不存在的 passive effect

那么這個(gè)時(shí)候就要去遍歷它的 child.fiber,也就是 <A/><B/>

首先拿到第一個(gè) fiber,也就是 <A/>,然后執(zhí)行 <A/>passive effect 返回的函數(shù),這步比較好理解

child = fiber.child;
if (child !== null) {
  nextEffect = child;
}

這里遍歷也是深度優(yōu)先,遍歷一個(gè) child,執(zhí)行一個(gè) passive effect 返回函數(shù),然后再遍歷下一個(gè) child(這邊 <A /> 已經(jīng)是葉子節(jié)點(diǎn)了)

然后拿到第二個(gè) fiber,也就是 <B/>,然后執(zhí)行 <B/>passive effect 返回的函數(shù),這步就不太好理解了

child = fiber.child;
if (child !== null) {
  nextEffect = child;
} else {
  commitPassiveUnmountEffectsInsideOfDeletedTree_complete(deletedSubtreeRoot);
}

這里要注意的是:

react 在尋找有 passive effectfiber 時(shí),只遍歷到有 passive effectfiber, 像 div 這種沒(méi)有 passive effect 就不會(huì)遍歷

但是在處理 deletions,react 會(huì)遍歷所有的 fiber,也就是說(shuō)從當(dāng)前的 fiber 開(kāi)始,一直往下遍歷到葉子節(jié)點(diǎn),這個(gè)葉子節(jié)點(diǎn)是指文本節(jié)點(diǎn)這種,往下不會(huì)有節(jié)點(diǎn)了(對(duì)于 A 組件來(lái)說(shuō) 文本A 是文本節(jié)點(diǎn))

然后在開(kāi)始往上遍歷,往上遍歷是調(diào)用 commitPassiveUnmountEffectsInsideOfDeletedTree_complete 函數(shù),直到遍歷到 deletionRoot,在向上遍歷的過(guò)程中會(huì)檢查是否有 sibling,如果有說(shuō)明 sibling 還沒(méi)被處理,這樣就找到了 <B/>,然后執(zhí)行 <B/>passive effect 返回的函數(shù)

如下圖所示:

useEffect返回函數(shù)執(zhí)行過(guò)程是什么

向下遍歷和向上遍歷

在處理 deletions 時(shí),對(duì)于每個(gè) deletedNode,都先向下遍歷,然后再向上遍歷

  • 向下遍歷:commitPassiveUnmountEffectsInsideOfDeletedTree_begin(深度優(yōu)先,優(yōu)先處理左邊的節(jié)點(diǎn))

  • 向上遍歷:commitPassiveUnmountEffectsInsideOfDeletedTree_complete(之后再處理右邊節(jié)點(diǎn))

總結(jié)

1. 遍歷 deletions 數(shù)組:

  • react 在處理 deletions 時(shí),先沿著 fiber tree 向下遍歷,如果有 passive effect 返回的函數(shù),則執(zhí)行

  • 一直遍歷到?jīng)]有 childfiber,再向上遍歷,處理 sibling

  • 再向上遍歷時(shí),如果如果遇到 sibling,再向下遍歷,向下遍歷時(shí)遇到 passive effect 返回的函數(shù),則執(zhí)行

  • 如此循環(huán)直到遍歷到 deletedNode,結(jié)束遍歷

2. 結(jié)合掌握 React 組件樹(shù)遍歷技巧

  • 遍歷尋找有 passive effect 節(jié)點(diǎn)

    • react 從根組件向下遍歷,如果沒(méi)有 passive effect,則不會(huì)遍歷

  • 遍歷時(shí),如果遇到當(dāng)前節(jié)點(diǎn)有 deletions 時(shí),會(huì)暫停尋找 passive effect 節(jié)點(diǎn)

    • 進(jìn)入遍歷 deletions 數(shù)組

react 遍歷 deletions 完整邏輯如下圖所示:

圖中綠色部分是遍歷 deletionsNode 過(guò)程,紅色部分是遍歷尋找 passive effect 過(guò)程

useEffect返回函數(shù)執(zhí)行過(guò)程是什么

讀到這里,這篇“useEffect返回函數(shù)執(zhí)行過(guò)程是什么”文章已經(jīng)介紹完畢,想要掌握這篇文章的知識(shí)點(diǎn)還需要大家自己動(dòng)手實(shí)踐使用過(guò)才能領(lǐng)會(huì),如果想了解更多相關(guān)內(nèi)容的文章,歡迎關(guān)注億速云行業(yè)資訊頻道。

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI