亚洲中字慕日产2020,大陆极品少妇内射AAAAAA,无码av大香线蕉伊人久久,久久精品国产亚洲av麻豆网站

資訊專欄INFORMATION COLUMN

React前端學習小結

iOS122 / 965人閱讀

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

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

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

React組件

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

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

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

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

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

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

掛載

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

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

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

更新

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

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

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

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

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

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

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

轉載請注明本文地址:http://www.ezyhdfw.cn/yun/102604.html

相關文章

  • 前端面試題小結

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

    fuchenxuan 評論0 收藏0
  • 前端面試題小結

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

    silenceboy 評論0 收藏0
  • 前端面試題小結

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

    wangtdgoodluck 評論0 收藏0
  • 7月份前端資源分享

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

    pingan8787 評論0 收藏0
  • 前端相關大雜燴

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

    fuyi501 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<