亚洲激情专区-91九色丨porny丨老师-久久久久久久女国产乱让韩-国产精品午夜小视频观看

溫馨提示×

溫馨提示×

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

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

如何使用React和DOM的節點刪除算法

發布時間:2021-10-19 17:25:55 來源:億速云 閱讀:205 作者:iii 欄目:web開發

本篇內容主要講解“如何使用React和DOM的節點刪除算法”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學習“如何使用React和DOM的節點刪除算法”吧!

Fiber架構使得React需要維護兩類樹結構,一類是Fiber樹,另一類是DOM樹。當刪除DOM節點時,Fiber樹也要同步變化。但請注意刪除操作執行的時機:在完成DOM節點的其他變化(增、改)前,要先刪除fiber節點,避免其他操作被干擾。 這是因為進行其他DOM操作時需要循環fiber樹,此時如果有需要刪除的fiber節點卻還沒刪除的話,就會發生混亂。

function commitMutationEffects(    firstChild: Fiber,    root: FiberRoot,    renderPriorityLevel,  ) {    let fiber = firstChild;    while (fiber !== null) {      // 首先進行刪除      const deletions = fiber.deletions;      if (deletions !== null) {        commitMutationEffectsDeletions(deletions, root, renderPriorityLevel);      }      // 如果刪除之后的fiber還有子節點,      // 遞歸調用commitMutationEffects來處理      if (fiber.child !== null) {        const primarySubtreeTag = fiber.subtreeTag & MutationSubtreeTag;        if (primarySubtreeTag !== NoSubtreeTag) {          commitMutationEffects(fiber.child, root, renderPriorityLevel);        }      }      if (__DEV__) {/*...*/} else {        // 執行其他DOM操作        try {          commitMutationEffectsImpl(fiber, root, renderPriorityLevel);        } catch (error) {          captureCommitPhaseError(fiber, error);        }      }      fiberfiber = fiber.sibling;    }  }

fiber.deletions是render階段的diff過程檢測到fiber的子節點如果有需要被刪除的,就會被加到這里來。

commitDeletion函數是刪除節點的入口,它通過調用unmountHostComponents實現刪除。搞懂刪除操作之前,先看看場景。

有如下的Fiber樹,Node(Node是一個代號,并不指的某個具體節點)節點即將被刪除。 

Fiber樹     div#root        |      <App/>        |       div        |     <Parent/>        |       Node        |     ↖        |       ↖        P &mdash;&mdash;&mdash;&mdash;&mdash;&mdash;> <Child>                    |                    a

通過這種場景可以推測出當刪除該節點時,它下面子樹中的所有節點都要被刪除。現在直接以這個場景為例,走一下刪除過程。這個過程實際上也就是unmountHostComponents函數的運行機制。

刪除過程

刪除Node節點需要父DOM節點的參與:

parentInstance.removeChild(child)

所以首先要定位到父級節點。過程是在Fiber樹中,以Node的父節點為起點往上找,找到的第一個原生DOM節點即為父節點。在例子中,父節點就是div。此后以Node為起點,遍歷子樹,子樹也是fiber樹,因此遍歷是深度優先遍歷,將每個子節點都刪除。

需要特別注意的一點是,對循環節點進行刪除,每個節點都會被刪除操作去處理,這里的每個節點是fiber節點而不是DOM節點。DOM節點的刪除時機是從Node開始遍歷進行刪除的時候,遇到了第一個原生DOM節點(HostComponent或HostText)這個時刻,在刪除了它子樹的所有fiber節點后,才會被刪除。

以上是完整過程的簡述,對于詳細過程要明確幾個關鍵函數的職責和調用關系才行。刪除fiber節點的是unmountHostComponents函數,被刪除的節點稱為目標節點,它的職責為:

  1. 鴻蒙官方戰略合作共建——HarmonyOS技術社區

  2.  找到目標節點的DOM層面的父節點

  3.  判斷目標節點如果是原生DOM類型的節點,那么執行3、4,否則先卸載自己之后再往下找到原生DOM類型的節點之后再執行3、4

  4.  遍歷子樹執行fiber節點的卸載

  5.  刪除目標節點的DOM節點

其中第3步的操作,是通過commitNestedUnmounts完成的,它的職責很單一也很明確,就是遍歷子樹卸載節點。

然后具體到每個節點的卸載過程,由commitUnmount完成。它的職責是

  1. 鴻蒙官方戰略合作共建——HarmonyOS技術社區

  2.  Ref的卸載

  3.  類組件生命周期的調用

  4.  HostPortal類型的fiber節點遞歸調用unmountHostComponents重復刪除過程

下面來看一下不同類型的組件它們的具體刪除過程是怎樣的。

區分被刪除組件的類別

Node節點的類型有多種可能性,我們以最典型的三種類型(HostComponent、ClassComponent、HostPortal)為例分別說明一下刪除過程。

首先執行unmountHostComponents,會向上找到DOM層面的父節點,然后根據下面的三種組件類型分別處理,我們挨個來看。

HostComponent

Node 是HostComponent,調用commitNestedUnmounts,以Node為起點,遍歷子樹,開始對所有子Fiber進行卸載操作,遍歷的過程是深度優先遍歷。

Delation   -->      Node(span)                       |    ↖                       |       ↖                       P &mdash;&mdash;&mdash;&mdash;&mdash;&mdash;> <Child>                                   |                                   a

對節點逐個執行commitUnmount進行卸載,這個遍歷過程其實對于三種類型的節點,都是類似的,為了節省篇幅,這里只表述一次。

Node的fiber被卸載,然后向下,p的fiber被卸載,p沒有child,找到它的sibling<Child>,<Child>的fiber被卸載,向下找到a,a的fiber被卸載。此時到了整個子樹的葉子節點,開始向上return。由a 到 <Child>,再回到Node,遍歷卸載的過程結束。

在子樹的所有fiber節點都被卸載之后,才可以安全地將Node的DOM節點從父節點中移除。

ClassComponent

Delation   -->      Node(ClassComponent)                       |                       |                      span                       |    ↖                       |       ↖                       P &mdash;&mdash;&mdash;&mdash;&mdash;&mdash;> <Child>                                   |                                   a

Node是ClassComponent,它沒有對應的DOM節點,要先調用commitUnmount卸載它自己,之后會先往下找,找到第一個原生DOM類型的節點span,以它為起點遍歷子樹,確保每一個fiber節點都被卸載,之后再將span從父節點中刪除。

HostPortal       

   div2(Container Of Node)             ↗  div   containerInfo   |    ↗   |  ↗  Node(HostPortal)   |   |  span   |    ↖   |       ↖   P &mdash;&mdash;&mdash;&mdash;&mdash;&mdash;> <Child>               |               a

Node是HostPortal,它沒有對應的DOM節點,因此刪除過程和ClassComponent基本一致,不同的是刪除它下面第一個子fiber的DOM節點時不是從這個被刪除的HostPortal類型節點的DOM層面的父節點中刪除,而是從HostPortal的containerInfo中移除,圖示上為div2,因為HostPortal會將子節點渲染到父組件以外的DOM節點。

以上是三種類型節點的刪除過程,這里值得注意的是,unmountHostComponents函數執行到遍歷子樹卸載每個節點的時候,一旦遇到HostPortal類型的子節點,會再次調用unmountHostComponents,以它為目標節點再進行它以及它子樹的卸載刪除操作,相當于一個遞歸過程。

commitUnmount

HostComponent 和 ClassComponent的刪除都調用了commitUnmount,除此之外還有FunctionComponent也會調用它。它的作用對三種組件是不同的:

  •  FunctionComponent 函數組件中一旦調用了useEffect,那么它卸載的時候要去調用useEffect的銷毀函數。(useLayoutEffect的銷毀函數是調用commitHookEffectListUnmount執行的)

  •  ClassComponent 類組件要調用componentWillUnmount

  •  HostComponent 要卸載ref 

function commitUnmount(    finishedRoot: FiberRoot,    current: Fiber,    renderPriorityLevel: ReactPriorityLevel,  ): void {    onCommitUnmount(current);    switch (current.tag) {      case FunctionComponent:      case ForwardRef:      case MemoComponent:      case SimpleMemoComponent:      case Block: {        const updateQueue: FunctionComponentUpdateQueue | null = (current.updateQueue: any);        if (updateQueue !== null) {          const lastEffect = updateQueue.lastEffect;          if (lastEffect !== null) {            const firstEffect = lastEffect.next;            let effect = firstEffect;            do {              const {destroy, tag} = effect;              if (destroy !== undefined) {                if ((tag & HookPassive) !== NoHookEffect) {                  // 向useEffect的銷毀函數隊列里push effect                  enqueuePendingPassiveHookEffectUnmount(current, effect);                } else {                  // 嘗試使用try...catch調用destroy                  safelyCallDestroy(current, destroy);                  ...                }              }              effecteffect = effect.next;            } while (effect !== firstEffect);          }        }        return;      }      case ClassComponent: {        safelyDetachRef(current);        const instance = current.stateNode;        // 調用componentWillUnmount        if (typeof instance.componentWillUnmount === 'function') {          safelyCallComponentWillUnmount(current, instance);        }        return;      }      case HostComponent: {        // 卸載ref        safelyDetachRef(current);        return;      }      ...    }  }

總結

我們來復盤一下刪除過程中的重點:

  •  刪除操作執行的時機

  •  刪除的目標是誰

  •  從哪里刪除

mutation在基于Fiber節點對DOM做其他操作之前,需要先刪除節點,保證留給后續操作的fiber節點都是有效的。刪除的目標是Fiber節點及其子樹和Fiber節點對應的DOM節點,整個軌跡循著fiber樹,對目標節點和所有子節點都進行卸載,對目標節點對應的(或之下的第一個)DOM節點進行刪除。對于原生DOM類型的節點,直接從其父DOM節點刪除,對于HostPortal節點,它會把子節點渲染到外部的DOM節點,所以會從這個DOM節點中刪除。明確以上三個點再結合上述梳理的過程,就可以逐漸理清刪除操作的脈絡。

到此,相信大家對“如何使用React和DOM的節點刪除算法”有了更深的了解,不妨來實際操作一番吧!這里是億速云網站,更多相關內容可以進入相關頻道進行查詢,關注我們,繼續學習!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

敖汉旗| 凤冈县| 郎溪县| 长武县| 扎赉特旗| 施秉县| 太白县| 阜新市| 梁河县| 扎鲁特旗| 延川县| 淮滨县| 从江县| 高青县| 钦州市| 永登县| 常山县| 安阳市| 聂拉木县| 富宁县| 旺苍县| 安岳县| 榆中县| 息烽县| 云阳县| 龙川县| 大庆市| 凤山市| 达尔| 清远市| 德安县| 团风县| 兴化市| 皮山县| 平顶山市| 阿拉善盟| 广宁县| 右玉县| 明光市| 荣昌县| 科尔|