成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

React和DOM的那些事-節點刪除算法

開發 前端 算法
本篇是詳細解讀React DOM操作的第一篇文章,文章所講的內容發生在commit階段。

[[378076]]

 本篇是詳細解讀React DOM操作的第一篇文章,文章所講的內容發生在commit階段。

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

  1. function commitMutationEffects(  
  2.   firstChild: Fiber,  
  3.   root: FiberRoot,  
  4.   renderPriorityLevel,  
  5. ) {  
  6.   let fiber = firstChild 
  7.   while (fiber !== null) { 
  8.      // 首先進行刪除  
  9.     const deletions = fiber.deletions;  
  10.     if (deletions !== null) {  
  11.       commitMutationEffectsDeletions(deletions, root, renderPriorityLevel);  
  12.     }  
  13.     // 如果刪除之后的fiber還有子節點,  
  14.     // 遞歸調用commitMutationEffects來處理  
  15.     if (fiber.child !== null) {  
  16.       const primarySubtreeTag = fiber.subtreeTag & MutationSubtreeTag;  
  17.       if (primarySubtreeTag !== NoSubtreeTag) {  
  18.         commitMutationEffects(fiber.child, root, renderPriorityLevel);  
  19.       }  
  20.     }  
  21.     if (__DEV__) {/*...*/} else {  
  22.       // 執行其他DOM操作  
  23.       try {  
  24.         commitMutationEffectsImpl(fiber, root, renderPriorityLevel);  
  25.       } catch (error) {  
  26.         captureCommitPhaseError(fiber, error);  
  27.       }  
  28.     }  
  29.     fiberfiber = fiber.sibling;  
  30.   }  

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

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

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

  1. Fiber樹  
  2.    div#root  
  3.       |  
  4.     <App/>  
  5.       |  
  6.      div  
  7.       |  
  8.    <Parent/>  
  9.       |  
  10.      Node  
  11.       |     ↖  
  12.       |       ↖  
  13.       P ——————> <Child>  
  14.                   |  
  15.                   a 

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

刪除過程

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

  1. parentInstance.removeChild(child) 

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

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

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

  1.  找到目標節點的DOM層面的父節點
  2.  判斷目標節點如果是原生DOM類型的節點,那么執行3、4,否則先卸載自己之后再往下找到原生DOM類型的節點之后再執行3、4
  3.  遍歷子樹執行fiber節點的卸載
  4.  刪除目標節點的DOM節點

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

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

  1.  Ref的卸載
  2.  類組件生命周期的調用
  3.  HostPortal類型的fiber節點遞歸調用unmountHostComponents重復刪除過程

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

區分被刪除組件的類別

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

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

HostComponent

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

  1. Delation   -->      Node(span)  
  2.                      |    ↖  
  3.                      |       ↖  
  4.                      P ——————> <Child>  
  5.                                  |  
  6.                                  a 

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

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

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

ClassComponent 

  1. Delation   -->      Node(ClassComponent)  
  2.                      |  
  3.                      |  
  4.                     span  
  5.                      |    ↖  
  6.                      |       ↖  
  7.                      P ——————> <Child>  
  8.                                  |  
  9.                                  a 

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

HostPortal         

  1.    div2(Container Of Node)  
  2.            ↗  
  3. div   containerInfo  
  4.  |    ↗  
  5.  |  ↗  
  6. Node(HostPortal)  
  7.  |  
  8.  |  
  9. span  
  10.  |    ↖  
  11.  |       ↖  
  12.  P ——————> <Child>  
  13.              |  
  14.              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 
  1. function commitUnmount(  
  2.   finishedRoot: FiberRoot,  
  3.   current: Fiber,  
  4.   renderPriorityLevel: ReactPriorityLevel,  
  5. ): void {  
  6.   onCommitUnmount(current);  
  7.   switch (current.tag) {  
  8.     case FunctionComponent:  
  9.     case ForwardRef:  
  10.     case MemoComponent:  
  11.     case SimpleMemoComponent:  
  12.     case Block: {  
  13.       const updateQueue: FunctionComponentUpdateQueue | null = (current.updateQueue: any);  
  14.       if (updateQueue !== null) {  
  15.         const lastEffect = updateQueue.lastEffect;  
  16.         if (lastEffect !== null) {  
  17.           const firstEffect = lastEffect.next;  
  18.           let effect = firstEffect 
  19.           do { 
  20.              const {destroy, tag} = effect;  
  21.             if (destroy !== undefined) {  
  22.               if ((tag & HookPassive) !== NoHookEffect) {  
  23.                 // 向useEffect的銷毀函數隊列里push effect  
  24.                 enqueuePendingPassiveHookEffectUnmount(current, effect);  
  25.               } else {  
  26.                 // 嘗試使用try...catch調用destroy  
  27.                 safelyCallDestroy(current, destroy);  
  28.                 ...  
  29.               }  
  30.             }  
  31.             effecteffect = effect.next;  
  32.           } while (effect !== firstEffect);  
  33.         }  
  34.       }  
  35.       return;  
  36.     }  
  37.     case ClassComponent: {  
  38.       safelyDetachRef(current);  
  39.       const instance = current.stateNode;  
  40.       // 調用componentWillUnmount  
  41.       if (typeof instance.componentWillUnmount === 'function') {  
  42.         safelyCallComponentWillUnmount(current, instance);  
  43.       }  
  44.       return;  
  45.     }  
  46.     case HostComponent: {  
  47.       // 卸載ref  
  48.       safelyDetachRef(current);  
  49.       return;  
  50.     }  
  51.     ...  
  52.   }  

總結

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

  •  刪除操作執行的時機
  •  刪除的目標是誰
  •  從哪里刪除

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

 

責任編輯:龐桂玉 來源: segmentfault
相關推薦

2022-07-19 13:31:18

Buddy算法內存管理框架

2020-10-07 22:21:13

程序員技術線程

2014-06-06 16:08:17

初志科技

2011-09-19 15:40:35

2020-07-29 08:14:59

云計算云遷移IT

2021-03-07 16:31:35

Java編譯反編譯

2018-12-26 13:22:05

NVMeNVMe-oF數據

2017-07-19 14:26:01

前端JavaScriptDOM

2011-05-19 16:47:50

軟件測試

2012-05-01 08:06:49

手機

2017-05-15 21:50:54

Linux引號

2024-02-04 17:03:30

2012-05-31 09:53:38

IT風云15年

2015-08-20 09:17:36

Java線程池

2015-09-14 09:28:47

2021-08-11 21:46:47

MySQL索引join

2017-03-08 08:53:44

Git命令 GitHub

2009-02-19 10:21:00

路由多WAN口

2012-10-08 11:55:05

2015-08-13 10:54:46

點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 一级片免费视频 | 国产98色在线 | 久久y| 狠狠爱免费视频 | 亚洲男人的天堂网站 | 毛片网站在线观看视频 | 久热精品在线 | 久久com | 成人av在线网站 | 青青草一区 | 视频在线一区 | 国产良家自拍 | 激情伊人网 | 欧美一二三 | 国产精品久久一区二区三区 | 亚洲欧美一区二区三区情侣bbw | 黄色日批视频 | 国产伦精品一区二区三区在线 | 欧美aa在线| 欧美日韩亚洲一区 | 亚洲精久| 麻豆一区二区三区 | 一级黄色片一级黄色片 | 一区二区在线观看免费视频 | 日韩在线免费视频 | 给我免费的视频在线观看 | 免费观看黄色一级片 | 国产真实乱对白精彩久久小说 | 一区二区三区视频在线观看 | 国产精品视屏 | 欧美成人精品一区 | 欧美精品日韩 | 久久精品一区二区视频 | 欧美在线不卡 | 伊人精品在线 | 一区二区三区视频免费看 | 成人在线免费网站 | 国产三级一区二区 | 成年人网站免费视频 | 久久午夜视频 | 亚洲精品在线观看网站 |