摘要:但是它與里大部分的概率是保持一致的。但是如何將轉(zhuǎn)換成函數(shù)的調(diào)用呢就是干這件事情的。好了,讓我們看看是如何工作的。下面的圖片在流程圖中高亮了一個(gè)組件是如何工作的最后希望這篇文章能幫助你理解是如何工作的至少在中
英文原文鏈接
Virtual DOM很神奇,同時(shí)也比較復(fù)雜,難以理解。react,preact和相似的js庫(kù)都使用了virtual dom。然而,我找不到任何好的文章或者文檔,可以詳細(xì)地又容易理解的方式來(lái)解釋它。因此我決定自己寫(xiě)一篇。
注意:文章篇幅較長(zhǎng),文中有大量的圖片來(lái)幫助理解。文中使用的是preact的代碼,因?yàn)樗w積小,容易閱讀。但是它與React里大部分的概率是保持一致的。希望閱讀完這篇文章后,你可以更好地理解React和Preact這樣的類庫(kù),甚至為它們作出貢獻(xiàn)。
在這篇文章中,我將列舉一個(gè)簡(jiǎn)單的例子來(lái)解釋以下這些是如何工作的:
Babel和JSX
創(chuàng)建VNode-一個(gè)簡(jiǎn)單的virtual DOM元素
處理組件和子組件
初始化渲染和創(chuàng)建一個(gè)DOM元素
重新渲染
移除DOM元素
替換DOM元素
The app這是一個(gè)簡(jiǎn)單地可篩選的搜索應(yīng)用,它包含了兩個(gè)組件FilteredList和List。List組件用來(lái)渲染一組items(默認(rèn):"California"和"New York")。這個(gè)應(yīng)用有一個(gè)搜索框,可以根據(jù)字母來(lái)過(guò)濾列表項(xiàng)。非常地直觀:
概覽圖我們用jsx來(lái)寫(xiě)組件,它會(huì)被babel轉(zhuǎn)換成純js,然后Preact的h函數(shù)會(huì)將這段js轉(zhuǎn)換成DOM樹(shù),最后Preact的Virtual DOM算法會(huì)將virtual DOM轉(zhuǎn)換成真實(shí)的DOM樹(shù),來(lái)構(gòu)建我們的應(yīng)用。
在深入Virtual DOM的生命周期之前,我們先理解一下jsx,因?yàn)樗鼮閹?kù)提供了入口。
Babel And JSX在React,Preact這樣的類庫(kù)中,沒(méi)有HTML標(biāo)簽,取而代之的是,一切都是javascript。所以我們要在js中寫(xiě)HTML標(biāo)簽,但是在js中寫(xiě)HTML簡(jiǎn)直就是噩夢(mèng)?
對(duì)于我們的應(yīng)用來(lái)說(shuō),我們將會(huì)像下面這樣來(lái)寫(xiě)HTML
這就是jsx的由來(lái)。jsx本質(zhì)上就是允許我們?cè)趈avascript中書(shū)寫(xiě)HTML!并且允許我們?cè)贖TML中通過(guò)使用花括號(hào)來(lái)使用js。
jsx幫助我們像下面這樣寫(xiě)組件
jsx很酷,但它不是合法的js,并且最終我們需要的是真實(shí)的DOM。JSX只是幫助編寫(xiě)一個(gè)真實(shí)DOM的替代品,除此之外,它別無(wú)用處。所以我們需要一種方法將它轉(zhuǎn)換成對(duì)應(yīng)的JSON對(duì)象(也就是Virtual DOM),作為轉(zhuǎn)化成真實(shí)DOM的輸入。我們需要一個(gè)函數(shù)來(lái)實(shí)現(xiàn)這個(gè)功能。
在Preact中h函數(shù)就是干這件事情的,等同于React中的React.createElement。
但是如何將jsx轉(zhuǎn)換成h函數(shù)的調(diào)用呢?Babel就是干這件事情的。Babel遍歷每個(gè)jsx節(jié)點(diǎn),并將它們轉(zhuǎn)換成h函數(shù)調(diào)用。
默認(rèn)情況下,Babel將jsx轉(zhuǎn)換成React.createElement調(diào)用
但是我們可以很容易地將函數(shù)名修改成任何名稱,只需要在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 */掛載到真實(shí)DOM
不僅僅是render中的代碼會(huì)被轉(zhuǎn)換成h函數(shù),最初的掛載也會(huì)!
這就是代碼執(zhí)行開(kāi)始的地方
//Mount to real DOM render(h函數(shù)的輸出, document.getElementById(‘a(chǎn)pp’)); //Converted to "h": render(h(FilteredList), document.getElementById(‘a(chǎn)pp’));
h函數(shù)將jsx轉(zhuǎn)化后的內(nèi)容轉(zhuǎn)換成Virtual DOM節(jié)點(diǎn)。一個(gè)Preact的Virtual DOM節(jié)點(diǎn)就是一個(gè)簡(jiǎn)單的代表了單個(gè)包含屬性和子節(jié)點(diǎn)的DOM節(jié)點(diǎn)的js對(duì)象,如下所示:
{ "nodeName": "", "attributes": {}, "children": [] }
比如,應(yīng)用的input標(biāo)簽對(duì)應(yīng)的Virtual DOM如下:
{ "nodeName": "input", "attributes": { "type": "text", "placeholder": "Search", "onChange": "" }, "children": [] }
注意:h函數(shù)并不是創(chuàng)建整棵樹(shù)!它只是簡(jiǎn)單地創(chuàng)建某個(gè)節(jié)點(diǎn)的js對(duì)象。但是因?yàn)?b>render方法。。。
好了,讓我們看看Virtual DOM是如何工作的。
Preact中的Virtual DOM算法在下面的流程圖中,展示了在Preact中,組件是如何被創(chuàng)建、更新和刪除的過(guò)程。同時(shí)也展示了像componentWillMount這樣的生命周期事件是什么時(shí)候被調(diào)用的。
現(xiàn)在理解起來(lái)有些困難,所以我們一步一步來(lái)拆解流程圖中的每種情況。
情景1:初始化app 1.1 創(chuàng)建Virtual DOM高亮的部分展示了根據(jù)給定的組件生成的Virtual DOM樹(shù)。注意一點(diǎn)這里并沒(méi)有為子組件創(chuàng)建Virtual DOM
下面這幅圖展示了應(yīng)用首次加載時(shí)發(fā)生的情況。這個(gè)庫(kù)最后為FilteredList組件創(chuàng)建了帶有子節(jié)點(diǎn)和屬性
的Virtual DOM
注意:在這個(gè)過(guò)程中還調(diào)用了componentWillMount和render生命周期方法(在上圖中的綠色區(qū)塊)
此時(shí),我們有了一個(gè)Virtual DOM,div元素是父親節(jié)點(diǎn),帶有一個(gè)input和一個(gè)list的子節(jié)點(diǎn)
1.2 如果不是一個(gè)組件,則創(chuàng)建真實(shí)的DOM在這一步中,它只是為父親節(jié)點(diǎn)創(chuàng)建一個(gè)真實(shí)DOM,對(duì)于子節(jié)點(diǎn),重復(fù)這個(gè)過(guò)程
此時(shí),我們?cè)谙聢D中只有一個(gè)div展示出來(lái)
在這一步中,循環(huán)所有的子節(jié)點(diǎn)。在我們的應(yīng)用中,將會(huì)循環(huán)input和list
在這一步中,我們將會(huì)處理葉子節(jié)點(diǎn),由于input有個(gè)父節(jié)點(diǎn)div,那么我們將會(huì)將input添加到div中作為
子節(jié)點(diǎn)。然后流程轉(zhuǎn)向創(chuàng)建List(第二個(gè)子節(jié)點(diǎn)是div)
此時(shí),我們的app長(zhǎng)下面這樣
注意:在input被創(chuàng)建之后,由于它沒(méi)有任何子節(jié)點(diǎn),并不會(huì)立馬就去循環(huán)和創(chuàng)建List組件。相反地,它會(huì)首先
把input標(biāo)簽添加到父節(jié)點(diǎn)div中去,完事之后再返回處理List標(biāo)簽
現(xiàn)在控制流回到了步驟1.1,并且開(kāi)始處理List組件。但是由于List是一個(gè)組件,所以它會(huì)遍歷執(zhí)行自身的render方法,從而獲得一組VNodes,就像下面這樣:
當(dāng)List組件的循環(huán)完成時(shí),它會(huì)返回List的VNode,就像下面這樣:
對(duì)于每個(gè)節(jié)點(diǎn),它將會(huì)重復(fù)以上的每一步。一旦到達(dá)葉子節(jié)點(diǎn),它將會(huì)被加入到父節(jié)點(diǎn)中去,并且重復(fù)這個(gè)過(guò)程。
下面的圖片展示了每個(gè)節(jié)點(diǎn)是如何添加上去的(深度優(yōu)先遍歷)
此時(shí)已經(jīng)完成了處理過(guò)程。然后對(duì)于所有的組件,會(huì)調(diào)用componentDidMount方法(從子組件開(kāi)始,直到父組件)
注意:當(dāng)一切準(zhǔn)備就緒,一個(gè)真實(shí)DOM的引用會(huì)被添加到每個(gè)組件的實(shí)例中。這個(gè)引用會(huì)在接下來(lái)的一些更新操作(創(chuàng)建、更新、刪除)被用來(lái)比較,避免重復(fù)創(chuàng)建相同的DOM節(jié)點(diǎn)
情景2:刪除葉子節(jié)點(diǎn)當(dāng)輸入"cal"并按回車,這將會(huì)刪除第二個(gè)列表子元素,也就是一個(gè)葉子節(jié)點(diǎn)(New York),同時(shí)其他父元素都會(huì)保留。
讓我們看下這種情景下,流程是怎么樣的
2.1 創(chuàng)建VNodes在初始化渲染之后,后面的每次改變都是一次"更新"。當(dāng)創(chuàng)建VNodes時(shí),更新周期與創(chuàng)建周期非常相似,并且再一次創(chuàng)建所有的VNodes。不過(guò)既然是更新(不是創(chuàng)建)組件,將會(huì)調(diào)用每個(gè)組件和子組件相應(yīng)的componentWillReceiveProps,shouldComponentUpdate和componentWillUpdate方法。
另外,更新周期并不會(huì)重新創(chuàng)建已經(jīng)存在的DOM元素。
之前提到過(guò),在初始化加載期間,每個(gè)組件都有一個(gè)指向真實(shí)DOM樹(shù)的引用。下面的圖展示了引用是如何尋找我們的應(yīng)用的。
當(dāng)VNodes被創(chuàng)建后,每個(gè)VNode的屬性都會(huì)與真實(shí)DOM的屬性相比較。如果真實(shí)DOM存在,循環(huán)將會(huì)轉(zhuǎn)移到下個(gè)節(jié)點(diǎn)
下面的圖展示了真實(shí)DOM和VNode之間的不同
由于存在不同,真實(shí)DOM中的"New York"節(jié)點(diǎn)會(huì)被算法刪除掉,正如下面圖展示的那樣。這個(gè)算法也稱為"componentDidUpdate"生命周期。
舉例:當(dāng)輸入blabla時(shí),由于不匹配"California"和"New York",我們將不會(huì)渲染子組件List。這意味著,我們需要卸載整個(gè)組件
刪除一個(gè)組件類似于刪除一個(gè)多帶帶的節(jié)點(diǎn)。除此之外,當(dāng)我們刪除一個(gè)包含組件引用的節(jié)點(diǎn),將會(huì)調(diào)用"componentWillUnmount",然后遞歸刪除所有的DOM元素。在刪除了所有的真實(shí)DOM元素之后,"componentDidUnmount"將會(huì)被調(diào)用。
下面的圖片展示了真實(shí)DOM元素"ul"包含了指向"List"組件的引用。
下面的圖片在流程圖中高亮了deleting/unmounting一個(gè)組件是如何工作的
希望這篇文章能幫助你理解Virtual DOM是如何工作的(至少在Preact中)
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/87098.html
摘要:與大多數(shù)全局對(duì)象不同,沒(méi)有構(gòu)造函數(shù)。為什么要設(shè)計(jì)更加有用的返回值早期寫(xiě)法寫(xiě)法函數(shù)式操作早期寫(xiě)法寫(xiě)法可變參數(shù)形式的構(gòu)造函數(shù)一般寫(xiě)法寫(xiě)法當(dāng)然還有很多,大家可以自行到上查看什么是代理設(shè)計(jì)模式代理模式,為其他對(duì)象提供一種代理以控制對(duì)這個(gè)對(duì)象的訪問(wèn)。 這是專門(mén)探索 JavaScript 及其所構(gòu)建的組件的系列文章的第 19 篇。 如果你錯(cuò)過(guò)了前面的章節(jié),可以在這里找到它們: 想閱讀更多優(yōu)質(zhì)文章請(qǐng)...
摘要:基礎(chǔ)創(chuàng)建虛擬參數(shù)元素名稱,例如參數(shù)屬性集合,例如,,,從參數(shù)開(kāi)始,表示該元素的子元素,通常這些元素通過(guò)創(chuàng)建,文本文件可以直接插入嘻嘻哈哈這是渲染器,將元素渲染到頁(yè)面中。 React簡(jiǎn)介 FeceBook開(kāi)源的一套框架,專注于MVC的視圖V模塊。實(shí)質(zhì)是對(duì)V視圖的一種實(shí)現(xiàn)。 React框架的設(shè)計(jì)沒(méi)有過(guò)分依賴于某個(gè)環(huán)境,它自建一套環(huán)境,就是virtual DOM(虛擬DOM)。 提供基礎(chǔ)AP...
摘要:中的元素組件實(shí)例和節(jié)點(diǎn),是中關(guān)系密切的個(gè)概念,也是很容易讓初學(xué)者迷惑的個(gè)概念。組件和元素關(guān)系密切,組件最核心的作用是返回元素。只有組件實(shí)例化后,每一個(gè)組件實(shí)例才有了自己的和,才持有對(duì)它的節(jié)點(diǎn)和子組件實(shí)例的引用。 React 深入系列,深入講解了React中的重點(diǎn)概念、特性和模式等,旨在幫助大家加深對(duì)React的理解,以及在項(xiàng)目中更加靈活地使用React。 React 中的元素、組件、實(shí)...
閱讀 1572·2021-11-22 13:52
閱讀 1378·2021-09-29 09:34
閱讀 2757·2021-09-09 11:40
閱讀 3059·2019-08-30 15:54
閱讀 1292·2019-08-30 15:53
閱讀 1004·2019-08-30 11:01
閱讀 1403·2019-08-29 17:22
閱讀 1979·2019-08-26 10:57