摘要:但是它與里大部分的概率是保持一致的。但是如何將轉換成函數的調用呢就是干這件事情的。好了,讓我們看看是如何工作的。下面的圖片在流程圖中高亮了一個組件是如何工作的最后希望這篇文章能幫助你理解是如何工作的至少在中
英文原文鏈接
Virtual DOM很神奇,同時也比較復雜,難以理解。react,preact和相似的js庫都使用了virtual dom。然而,我找不到任何好的文章或者文檔,可以詳細地又容易理解的方式來解釋它。因此我決定自己寫一篇。
注意:文章篇幅較長,文中有大量的圖片來幫助理解。文中使用的是preact的代碼,因為它體積小,容易閱讀。但是它與React里大部分的概率是保持一致的。希望閱讀完這篇文章后,你可以更好地理解React和Preact這樣的類庫,甚至為它們作出貢獻。
在這篇文章中,我將列舉一個簡單的例子來解釋以下這些是如何工作的:
Babel和JSX
創建VNode-一個簡單的virtual DOM元素
處理組件和子組件
初始化渲染和創建一個DOM元素
重新渲染
移除DOM元素
替換DOM元素
The app這是一個簡單地可篩選的搜索應用,它包含了兩個組件FilteredList和List。List組件用來渲染一組items(默認:"California"和"New York")。這個應用有一個搜索框,可以根據字母來過濾列表項。非常地直觀:
概覽圖我們用jsx來寫組件,它會被babel轉換成純js,然后Preact的h函數會將這段js轉換成DOM樹,最后Preact的Virtual DOM算法會將virtual DOM轉換成真實的DOM樹,來構建我們的應用。
在深入Virtual DOM的生命周期之前,我們先理解一下jsx,因為它為庫提供了入口。
Babel And JSX在React,Preact這樣的類庫中,沒有HTML標簽,取而代之的是,一切都是javascript。所以我們要在js中寫HTML標簽,但是在js中寫HTML簡直就是噩夢?
對于我們的應用來說,我們將會像下面這樣來寫HTML
這就是jsx的由來。jsx本質上就是允許我們在javascript中書寫HTML!并且允許我們在HTML中通過使用花括號來使用js。
jsx幫助我們像下面這樣寫組件
jsx很酷,但它不是合法的js,并且最終我們需要的是真實的DOM。JSX只是幫助編寫一個真實DOM的替代品,除此之外,它別無用處。所以我們需要一種方法將它轉換成對應的JSON對象(也就是Virtual DOM),作為轉化成真實DOM的輸入。我們需要一個函數來實現這個功能。
在Preact中h函數就是干這件事情的,等同于React中的React.createElement。
但是如何將jsx轉換成h函數的調用呢?Babel就是干這件事情的。Babel遍歷每個jsx節點,并將它們轉換成h函數調用。
默認情況下,Babel將jsx轉換成React.createElement調用
但是我們可以很容易地將函數名修改成任何名稱,只需要在babelrc中配置一下即可
Option 1: //.babelrc { "plugins": [ ["transform-react-jsx", { "pragma": "h" }] ] } Option 2: //Add the below comment as the 1st line in every JSX file /** @jsx h */掛載到真實DOM
不僅僅是render中的代碼會被轉換成h函數,最初的掛載也會!
這就是代碼執行開始的地方
//Mount to real DOM render(h函數的輸出, document.getElementById(‘app’)); //Converted to "h": render(h(FilteredList), document.getElementById(‘app’));
h函數將jsx轉化后的內容轉換成Virtual DOM節點。一個Preact的Virtual DOM節點就是一個簡單的代表了單個包含屬性和子節點的DOM節點的js對象,如下所示:
{ "nodeName": "", "attributes": {}, "children": [] }
比如,應用的input標簽對應的Virtual DOM如下:
{ "nodeName": "input", "attributes": { "type": "text", "placeholder": "Search", "onChange": "" }, "children": [] }
注意:h函數并不是創建整棵樹!它只是簡單地創建某個節點的js對象。但是因為render方法。。。
好了,讓我們看看Virtual DOM是如何工作的。
Preact中的Virtual DOM算法在下面的流程圖中,展示了在Preact中,組件是如何被創建、更新和刪除的過程。同時也展示了像componentWillMount這樣的生命周期事件是什么時候被調用的。
現在理解起來有些困難,所以我們一步一步來拆解流程圖中的每種情況。
情景1:初始化app 1.1 創建Virtual DOM高亮的部分展示了根據給定的組件生成的Virtual DOM樹。注意一點這里并沒有為子組件創建Virtual DOM
下面這幅圖展示了應用首次加載時發生的情況。這個庫最后為FilteredList組件創建了帶有子節點和屬性
的Virtual DOM
注意:在這個過程中還調用了componentWillMount和render生命周期方法(在上圖中的綠色區塊)
此時,我們有了一個Virtual DOM,div元素是父親節點,帶有一個input和一個list的子節點
1.2 如果不是一個組件,則創建真實的DOM在這一步中,它只是為父親節點創建一個真實DOM,對于子節點,重復這個過程
此時,我們在下圖中只有一個div展示出來
在這一步中,循環所有的子節點。在我們的應用中,將會循環input和list
在這一步中,我們將會處理葉子節點,由于input有個父節點div,那么我們將會將input添加到div中作為
子節點。然后流程轉向創建List(第二個子節點是div)
此時,我們的app長下面這樣
注意:在input被創建之后,由于它沒有任何子節點,并不會立馬就去循環和創建List組件。相反地,它會首先
把input標簽添加到父節點div中去,完事之后再返回處理List標簽
現在控制流回到了步驟1.1,并且開始處理List組件。但是由于List是一個組件,所以它會遍歷執行自身的render方法,從而獲得一組VNodes,就像下面這樣:
當List組件的循環完成時,它會返回List的VNode,就像下面這樣:
對于每個節點,它將會重復以上的每一步。一旦到達葉子節點,它將會被加入到父節點中去,并且重復這個過程。
下面的圖片展示了每個節點是如何添加上去的(深度優先遍歷)
此時已經完成了處理過程。然后對于所有的組件,會調用componentDidMount方法(從子組件開始,直到父組件)
注意:當一切準備就緒,一個真實DOM的引用會被添加到每個組件的實例中。這個引用會在接下來的一些更新操作(創建、更新、刪除)被用來比較,避免重復創建相同的DOM節點
情景2:刪除葉子節點當輸入"cal"并按回車,這將會刪除第二個列表子元素,也就是一個葉子節點(New York),同時其他父元素都會保留。
讓我們看下這種情景下,流程是怎么樣的
2.1 創建VNodes在初始化渲染之后,后面的每次改變都是一次"更新"。當創建VNodes時,更新周期與創建周期非常相似,并且再一次創建所有的VNodes。不過既然是更新(不是創建)組件,將會調用每個組件和子組件相應的componentWillReceiveProps,shouldComponentUpdate和componentWillUpdate方法。
另外,更新周期并不會重新創建已經存在的DOM元素。
之前提到過,在初始化加載期間,每個組件都有一個指向真實DOM樹的引用。下面的圖展示了引用是如何尋找我們的應用的。
當VNodes被創建后,每個VNode的屬性都會與真實DOM的屬性相比較。如果真實DOM存在,循環將會轉移到下個節點
下面的圖展示了真實DOM和VNode之間的不同
由于存在不同,真實DOM中的"New York"節點會被算法刪除掉,正如下面圖展示的那樣。這個算法也稱為"componentDidUpdate"生命周期。
舉例:當輸入blabla時,由于不匹配"California"和"New York",我們將不會渲染子組件List。這意味著,我們需要卸載整個組件
刪除一個組件類似于刪除一個多帶帶的節點。除此之外,當我們刪除一個包含組件引用的節點,將會調用"componentWillUnmount",然后遞歸刪除所有的DOM元素。在刪除了所有的真實DOM元素之后,"componentDidUnmount"將會被調用。
下面的圖片展示了真實DOM元素"ul"包含了指向"List"組件的引用。
下面的圖片在流程圖中高亮了deleting/unmounting一個組件是如何工作的
希望這篇文章能幫助你理解Virtual DOM是如何工作的(至少在Preact中)
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/87098.html
摘要:與大多數全局對象不同,沒有構造函數。為什么要設計更加有用的返回值早期寫法寫法函數式操作早期寫法寫法可變參數形式的構造函數一般寫法寫法當然還有很多,大家可以自行到上查看什么是代理設計模式代理模式,為其他對象提供一種代理以控制對這個對象的訪問。 這是專門探索 JavaScript 及其所構建的組件的系列文章的第 19 篇。 如果你錯過了前面的章節,可以在這里找到它們: 想閱讀更多優質文章請...
摘要:中的元素組件實例和節點,是中關系密切的個概念,也是很容易讓初學者迷惑的個概念。組件和元素關系密切,組件最核心的作用是返回元素。只有組件實例化后,每一個組件實例才有了自己的和,才持有對它的節點和子組件實例的引用。 React 深入系列,深入講解了React中的重點概念、特性和模式等,旨在幫助大家加深對React的理解,以及在項目中更加靈活地使用React。 React 中的元素、組件、實...
閱讀 1459·2021-11-22 13:52
閱讀 1281·2021-09-29 09:34
閱讀 2690·2021-09-09 11:40
閱讀 3031·2019-08-30 15:54
閱讀 1255·2019-08-30 15:53
閱讀 971·2019-08-30 11:01
閱讀 1354·2019-08-29 17:22
閱讀 1943·2019-08-26 10:57