国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

React前端學(xué)習(xí)小結(jié)

iOS122 / 912人閱讀

摘要:正式開始系統(tǒng)地學(xué)習(xí)前端已經(jīng)三個(gè)多月了,感覺前端知識(shí)體系龐雜但是又非常有趣。更新一個(gè)節(jié)點(diǎn)需要做的事情有兩件,更新頂層標(biāo)簽的屬性,更新這個(gè)標(biāo)簽包裹的子節(jié)點(diǎn)。

正式開始系統(tǒng)地學(xué)習(xí)前端已經(jīng)三個(gè)多月了,感覺前端知識(shí)體系龐雜但是又非常有趣。前端演進(jìn)到現(xiàn)在對(duì)開發(fā)人員的代碼功底要求已經(jīng)越來越高,幾年前的前端開發(fā)還是大量操作DOM,直接與用戶交互,而React、Vue等MVVM框架的出現(xiàn),則幫助開發(fā)者從DOM中解放出來,將關(guān)注點(diǎn)轉(zhuǎn)移到數(shù)據(jù)上來,也使前端開發(fā)愈發(fā)工程化和規(guī)范化。我入門的第一個(gè)MVVM框架是React,正所謂知其然更要知其所以然,再加上本身也對(duì)React中的虛擬DOM之類的新奇玩意兒非常感興趣,因此最近兩星期在工作間隙參照著幾篇非常棒的博客,淺讀了React16.0.0的源碼,在此把一些感想分享出來,由于水平有限,如果有什么理解不正確的話,歡迎交流與指正。

由于網(wǎng)上的博客已經(jīng)有了非常詳細(xì)的代碼分析,因此我在此不再貼代碼細(xì)節(jié),只是進(jìn)行一下簡(jiǎn)單的梳理,建議各位看官可以參閱下面兩個(gè)系列的文章,我覺得寫得非常好:
React源碼分析系列
React源碼解析
另外,如果你還沒有學(xué)習(xí)過react,強(qiáng)烈建議你跟著下面鏈接里的教程走進(jìn)React世界:
React小書
下面開始我的分享了

React組件

React開發(fā)者干的事情很簡(jiǎn)單,就是玩弄組件。而從你寫下class XXX extend Component再到你的組件成功渲染在真實(shí)DOM上,中間其實(shí)經(jīng)歷了一個(gè)復(fù)雜的過程。其中涉及三個(gè)重要的對(duì)象,可以認(rèn)為是React的核心,這三個(gè)對(duì)象形象地說就是三種視角下的React組件。

首先,對(duì)開發(fā)人員來說,React組件就是ReactClass,我們通過class XXX extend Component(ES5是調(diào)用createClass函數(shù))這種方式構(gòu)建我們自己的組件,在組件內(nèi)部,我們可以隨心所欲的玩弄state,props,生命周期函數(shù)等,最終目的是根據(jù)需要,在render函數(shù)中return 一個(gè)我們需要的HTML模板,最終掛載到DOM樹上,而中間這個(gè)過程,則需要涉及React中的另外兩個(gè)核心對(duì)象。

React如此風(fēng)靡的原因在于它幫助開發(fā)人員從DOM中解放出來,不是直接操作DOM,而是操作React的Virtual-DOM,然后通過強(qiáng)大的diff算法,先更新Virtual-DOM,然后最合理高效地更新實(shí)際DOM。因此在render函數(shù)中,我們最后return的并非實(shí)際的DOM元素,事實(shí)上,如果不用JSX的語法,我們最后實(shí)際上是調(diào)用了createElement方法,return 出一個(gè)ReactElement對(duì)象——這就是React組件在內(nèi)存中的存在方式。

ReactElement內(nèi)部含type,key,context, props四個(gè)關(guān)鍵屬性。用過React的人應(yīng)該很熟悉后三個(gè),而type則用于標(biāo)識(shí)組件的類型,type字段如果是字符串(如“div”,“p”等),則表示組件對(duì)應(yīng)的是一個(gè)實(shí)際DOM對(duì)象,如div,p等,如果type字段是ReactClass的構(gòu)造函數(shù),則表示組件是我們自定義的。因此傳說中的Virtual-DOM實(shí)質(zhì)就是各種ReactElement構(gòu)成的javaScript對(duì)象樹,是實(shí)際DOM的ReactElement對(duì)象映射。然而ReactElement相當(dāng)于只是數(shù)據(jù)的容器,它無法更改數(shù)據(jù),因此我們還需要一個(gè)數(shù)據(jù)的操作者,這個(gè)操作者就是ReactComponent,它就是React系統(tǒng)眼里的組件。

根據(jù)組件類型的不同,ReactComponent又分為四種:ReactDOMTextComponent(后文中記為RTC)、ReactDOMComponent(后文中記為RDC)、ReactCompositeComponent(后文中記為RCC)、ReactDOMEmptyComponent(后文中記為REC),具體含義從名字就可以判斷出來。這四種ReactComponent是通過一個(gè)工廠函數(shù)instantiaReactComponent生成的,它接收一個(gè)node參數(shù),如果node是null,則生成ReactDOMEmptyComponent,如果node是數(shù)字或字符串,則生成eactDOMTextComponent,如果傳入的node是一個(gè)對(duì)象,沒錯(cuò),你肯定猜到了這個(gè)對(duì)象正是ReactElement對(duì)象,我們則可以通過它的type屬性判斷是普通DOM元素還是自定義的組件,由此分別生成ReactDOMComponent和ReactCompositeComponent,這四種ReactComponent雖然是不同的對(duì)象,但是都實(shí)現(xiàn)了mountComponent,receiveComponent和unmountComponent三個(gè)關(guān)鍵的方法,mountComponent方法用于把ReactElement轉(zhuǎn)化為HTML標(biāo)記,最終掛載到DOM上,而經(jīng)瀏覽器解析后的DOM元素,就是用戶視角看到的React組件了。receiveComponent方法接收新的組件信息,用于更新組件,unmountComponent方法則顯然是卸載組件用的,不同的Component對(duì)這三個(gè)方法有不同的實(shí)現(xiàn)方式,但是都提供了名字相同的接口,其實(shí)有點(diǎn)類似java中的多態(tài)。

值得一提的是在mountComponent被調(diào)用時(shí),ReactComponent標(biāo)記了_currentElement, _instance兩個(gè)內(nèi)部屬性,用于記錄與之關(guān)聯(lián)的ReactElement和ReactClass實(shí)例,這兩個(gè)屬性非常重要,它們是把React中的幾個(gè)核心對(duì)象聯(lián)系起來的橋梁。我把React中三個(gè)核心對(duì)象之間的聯(lián)系表示成上面的框圖,從開發(fā)者構(gòu)造出組件,再到最后渲染出DOM元素展示給用戶,正是沿著紅色的路徑實(shí)現(xiàn)的。

掛載

前面提到,從ReactElement到實(shí)際的HTML標(biāo)記,是通過ReactComponent的mountComponent方法實(shí)現(xiàn)的,文本節(jié)點(diǎn)和空節(jié)點(diǎn)暫且不談,我們關(guān)注一下自定義組件和DOM元素的掛載方法。同樣用框圖的形式展現(xiàn)出來。

首先看RCC,在掛載初期,把ReactElement(后文稱element)和對(duì)應(yīng)的ReactClass實(shí)例(后文稱instance)放入ReactInstanceMap中,留給以后使用,然后在performInitialMount方法中才進(jìn)行真正的掛載過程,這個(gè)方法中先調(diào)用其對(duì)應(yīng)instance的componentWillMount方法,然后調(diào)用render方法生成一個(gè)新的element,將render出的element傳InstantiateReactCompoentn方法,生成對(duì)應(yīng)的Component實(shí)例,然后接著調(diào)用該實(shí)例的mountComponent方法即可。沒錯(cuò),這是一個(gè)遞歸的過程,你發(fā)現(xiàn)componentWilMount方法被放在了子元素的mount方法之前,componentDidMount方法被放在了子元素mount方法之后,因此在遞歸調(diào)用過程中,父元素的componentWillMount方法總是在子元素的componentWillMount方法之前被調(diào)用,而父元素的componentDidMount方法則總是在子元素的componentDidMount方法之后被調(diào)用。另外,你一定也看到了‘偽多態(tài)’的好處,你不用管render出來的是什么類型的元素,反正直接甩鍋調(diào)用它的mountComponent方法就行了,因此,RCC的mount過程,實(shí)際最后是落實(shí)到另外三個(gè)component的mountComponent方法去生成HTML標(biāo)記的。我們?cè)賮砜匆幌翿DC的mountComponent方法,在實(shí)際源碼中,DOM元素的掛載和更新方法都隱藏得比較深,調(diào)用鏈很長(zhǎng),我建議大家如果有興趣的話直接看我分享的第二個(gè)鏈接里的簡(jiǎn)易版實(shí)現(xiàn),其大致流程大致如框圖中所示,比較清晰了,只需要知道在拼接屬性的時(shí)候需要對(duì)事件屬性多帶帶處理,因?yàn)镽eact實(shí)現(xiàn)了一套合成事件系統(tǒng),盡可能實(shí)現(xiàn)了瀏覽器兼容。然后,你會(huì)發(fā)現(xiàn)DOM元素的mout也是一個(gè)遞歸過程,獲取當(dāng)前元素的標(biāo)簽名和屬性后,標(biāo)簽里的內(nèi)容又交給子元素的mountComponent方法去實(shí)現(xiàn)即可。

看到這里,你會(huì)發(fā)現(xiàn)RCC和RDC的mountComponent方法核心都是兩個(gè)字——遞歸,對(duì)子元素遞歸調(diào)用mountComponent方法。根元素經(jīng)過這個(gè)過程之后,就能得到一個(gè)完整的DOM樹,再把根節(jié)點(diǎn)通過ReactDOM.render方法插入容器中即可,這個(gè)不再詳述了。

更新

剛才說到組件的掛載過程實(shí)際核心就是遞歸調(diào)用子組件的掛載過程,接下來你會(huì)發(fā)現(xiàn),組件的更新,實(shí)質(zhì)也是通過遞歸完成的。先從比較簡(jiǎn)單的RCC看起

不要被這些亂七八糟的線條嚇到,其實(shí)自定義組件的更新過程并不復(fù)雜。首先,該方法接收一個(gè)新的ReactElement,組件什么時(shí)候會(huì)更新呢?有兩種情況,一種是組件接收到上層組件傳來的新props,這種時(shí)候新的ReactElement的props字段和舊element是不同的。另一種情況是在組件內(nèi)部調(diào)用了setState方法,由于ReactElement里面不保存state,因此這種情況下新舊ReactElement是相同的,根據(jù)這個(gè)特點(diǎn),可以判斷是否調(diào)用componentWillReceiveProps方法。接下來調(diào)用實(shí)例中的shouldUpdateComponent方法,如果該方法return值為false或者開發(fā)人員沒有寫這個(gè)方法,就會(huì)調(diào)用接下來的渲染和子更新過程,如果該方法值為true,那么更新過程在這里就終止了,不會(huì)調(diào)用接下來的渲染和子更新。這個(gè)特性使得我們能通過這個(gè)方法手動(dòng)決定是否要進(jìn)行渲染,達(dá)到提升性能的效果。接著往下走,調(diào)用了componentWillUpdate方法后,instance實(shí)例將根據(jù)傳入的新element更新props,然后把state的值更新為最新的(setState過程將在后文中分析),這時(shí)候再調(diào)用render方法,就能得到一個(gè)最新的renderdElement方法了,看過了掛載的過程你可能已經(jīng)想到了接下來只需要把這個(gè)新的renderdElement傳入子元素的receiveComponent方法中即可。但是其實(shí)在這一步之前還有一個(gè)判斷的過程,這個(gè)過程封裝在名字叫shouldeUpdateReactComponent方法中,該方法非常重要,在后面RDC的更新過程中也會(huì)用到,它接收兩個(gè)element參數(shù),判斷這兩個(gè)element是否key和type都相同,如果是則返回true,否則返回false。在RCC的更新過程中,會(huì)把未更新時(shí)render出的element和最新render出的element作為參數(shù)傳入該方法,如果比較結(jié)果為true,則對(duì)render出的子元素生成的component遞歸調(diào)用receiveComponent方法,否則,直接對(duì)當(dāng)前組件先卸載再重新掛載。由于一個(gè)自定義組件render出的元素只有一個(gè),不可能是數(shù)組,因此對(duì)RCC來說,這個(gè)比對(duì)過程中其實(shí)key的意義不大,關(guān)鍵還是比對(duì)type是否一致,因此RCC更新過程就是,如果最新render出的元素與之前render的元素類型相同,比如原來render出的element是div,狀態(tài)更新之后render出來還是div,那就對(duì)這個(gè)div繼續(xù)深入更新,否則,直接先卸載當(dāng)前的組件,再重新掛載一個(gè)最新的。

接下來再看RDC的更新過程,在源碼中,RDC的更新過程同樣被包裝和隱藏得比較深,建議大家同樣參看第二個(gè)鏈接里的源碼分析系列。

更新一個(gè)DOM節(jié)點(diǎn)需要做的事情有兩件,1. 更新頂層標(biāo)簽的屬性,2. 更新這個(gè)標(biāo)簽包裹的子節(jié)點(diǎn)。更新屬性比較容易理解,大家參閱一下代碼很容易看懂,重點(diǎn)是對(duì)子節(jié)點(diǎn)的更新。

子節(jié)點(diǎn)的更新過程做的事情也只有兩件:1. 找出狀態(tài)更新后DOM樹與狀態(tài)更新前DOM樹的所有不同,把這些差異按類型組裝成差異對(duì)象放入diffQueue隊(duì)列中,差異對(duì)象有INSERT_MARKUP, MOVE_EXISTING, REMOVE_NODE, SET_MARKUP, TEXT_CONTENT幾種,這個(gè)過程稱為diff,2.從diffQueue中依次取出差異對(duì)象,在真實(shí)DOM樹中完成變更??傊褪桥空也町悾啃薷腄OM樹。我們?cè)賮砜磳?shí)現(xiàn)細(xì)節(jié),首先,既然要更新子元素,肯定得拿到子元素的Component實(shí)例,flattenChildren函數(shù)做的正是這個(gè)工作,我們知道一個(gè)元素的子元素,在ReactElement中存儲(chǔ)在props.children數(shù)組中,flattenChildren把這個(gè)childrent數(shù)組中的element通通轉(zhuǎn)化為component存在了一個(gè)map中,這個(gè)map中的鍵是什么呢?如果我們?cè)谑褂媒M件實(shí)例的時(shí)候傳入了key屬性,那么map的鍵就是我們傳入的這個(gè)key,否則,這個(gè)map的鍵就是children數(shù)組的下標(biāo)(看到這里,就可以思考一下key的作用是什么了,在此我不繼續(xù)展開,后續(xù)會(huì)寫新的博文詳細(xì)講述)。接下我們需要調(diào)用generateComponentChildren方法,該方法接收的參數(shù)是傳入的新的子element合集,它的內(nèi)部做了什么呢?還是和對(duì)待老childElements一樣,先對(duì)每個(gè)element生成鍵,然后關(guān)鍵來了,我們將從剛才flattenChildren生成的舊components中,取出同鍵component,然后得到該component的element,記得RCC更新時(shí)候調(diào)用的shouldUpdateReactComponent方法嗎,我們又要重新調(diào)用它了。如果type相同則復(fù)用舊的component,執(zhí)行receiveComponent方法遞歸更新,否則就生成新的Component,最后同樣得到一個(gè)component map,這個(gè)新的map中的鍵還是一樣的,如果傳入了key字段,那么鍵就是key,否則鍵就是數(shù)組下標(biāo),而這個(gè)map的值是component,這些component中,有的是還是上一狀態(tài)的component,只是執(zhí)行了更新而已,有的則是新生成的component。生成這個(gè)新的component集合之后,我們就可以比對(duì)前后兩次的同鍵component,如果兩個(gè)component相同,說明我們?cè)诒緦訌?fù)用了之前的狀態(tài)未更新前的組件,那么我們只需要移動(dòng)之前的組件即可,否則說明我們?cè)诒緦硬荒苡弥暗慕M件了,那么我們就需要?jiǎng)h除舊組件,插入新組件。我們暫時(shí)不執(zhí)行這些操作,而是先把這些差異組裝成對(duì)象放到隊(duì)列中,到最后統(tǒng)一更新即可,這個(gè)統(tǒng)一更新的過程就是patch,在此不再詳述。

這一部分相對(duì)比較難理解,但也正是React diff算法的核心,不知你有沒有發(fā)現(xiàn),我們比對(duì)差異對(duì)象只在同一層之間對(duì)比,這正是React diff算法高效的原因,因?yàn)樵赪eb中,對(duì)DOM樹的操作很少跨層,因此只比對(duì)同層差異,就可以只遍歷一遍所有節(jié)點(diǎn)就找出所有差異,算法復(fù)雜度只有O(n)。當(dāng)然了,帶來的問題就是如果真的出現(xiàn)了跨層移動(dòng)節(jié)點(diǎn)的操作,我們就沒法復(fù)用這個(gè)節(jié)點(diǎn),而是得先卸載再掛載,但是在幾乎不會(huì)有跨層操作的DOM樹中來說,這點(diǎn)代價(jià)與對(duì)性能的提升相比是很小的。要理解這一部分需要對(duì)遞歸理解比較透徹,建議大家對(duì)著代碼仔細(xì)捋一捋。

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/102604.html

相關(guān)文章

  • 前端面試題小結(jié)

    摘要:如何解決不同終端的適配問題彈性盒子,非常不錯(cuò)的選擇的運(yùn)行流程生命周期生命周期優(yōu)化解釋中虛擬存在的好處為什么可以解決跨域問題地址欄輸入流程總結(jié)初級(jí)階段是會(huì)用。 前幾天也是有人問我的一些問題,我覺得還是挺有了解價(jià)值的,也是一些平時(shí)開發(fā)可能比較會(huì)忽略的問題。別的不多說,直接開門見山: 1.post和get的區(qū)別? 我們都知道GET和POST是HTTP請(qǐng)求的兩種基本方法。我相信如果有人問到你這...

    fuchenxuan 評(píng)論0 收藏0
  • 前端面試題小結(jié)

    摘要:如何解決不同終端的適配問題彈性盒子,非常不錯(cuò)的選擇的運(yùn)行流程生命周期生命周期優(yōu)化解釋中虛擬存在的好處為什么可以解決跨域問題地址欄輸入流程總結(jié)初級(jí)階段是會(huì)用。 前幾天也是有人問我的一些問題,我覺得還是挺有了解價(jià)值的,也是一些平時(shí)開發(fā)可能比較會(huì)忽略的問題。別的不多說,直接開門見山: 1.post和get的區(qū)別? 我們都知道GET和POST是HTTP請(qǐng)求的兩種基本方法。我相信如果有人問到你這...

    silenceboy 評(píng)論0 收藏0
  • 前端面試題小結(jié)

    摘要:如何解決不同終端的適配問題彈性盒子,非常不錯(cuò)的選擇的運(yùn)行流程生命周期生命周期優(yōu)化解釋中虛擬存在的好處為什么可以解決跨域問題地址欄輸入流程總結(jié)初級(jí)階段是會(huì)用。 前幾天也是有人問我的一些問題,我覺得還是挺有了解價(jià)值的,也是一些平時(shí)開發(fā)可能比較會(huì)忽略的問題。別的不多說,直接開門見山: 1.post和get的區(qū)別? 我們都知道GET和POST是HTTP請(qǐng)求的兩種基本方法。我相信如果有人問到你這...

    wangtdgoodluck 評(píng)論0 收藏0
  • 7月份前端資源分享

    摘要:更多資源請(qǐng)文章轉(zhuǎn)自月份前端資源分享的作用數(shù)組元素隨機(jī)化排序算法實(shí)現(xiàn)學(xué)習(xí)筆記數(shù)組隨機(jī)排序個(gè)變態(tài)題解析上個(gè)變態(tài)題解析下中的數(shù)字前端開發(fā)筆記本過目不忘正則表達(dá)式聊一聊前端存儲(chǔ)那些事兒一鍵分享到各種寫給剛?cè)腴T的前端工程師的前后端交互指南物聯(lián)網(wǎng)世界的 更多資源請(qǐng)Star:https://github.com/maidishike... 文章轉(zhuǎn)自:https://github.com/jsfr...

    pingan8787 評(píng)論0 收藏0
  • 前端相關(guān)大雜燴

    摘要:希望幫助更多的前端愛好者學(xué)習(xí)。前端開發(fā)者指南作者科迪林黎,由前端大師傾情贊助。翻譯最佳實(shí)踐譯者張捷滬江前端開發(fā)工程師當(dāng)你問起有關(guān)與時(shí),老司機(jī)們首先就會(huì)告訴你其實(shí)是個(gè)沒有網(wǎng)絡(luò)請(qǐng)求功能的庫(kù)。 前端基礎(chǔ)面試題(JS部分) 前端基礎(chǔ)面試題(JS部分) 學(xué)習(xí) React.js 比你想象的要簡(jiǎn)單 原文地址:Learning React.js is easier than you think 原文作...

    fuyi501 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<