React組件優(yōu)化篇二 · 調(diào)和過程

如果你能干的父母把你生的天生迎合這個世界十减,就是莫大的幸福了栈幸。萬一沒把你生得適應(yīng)這個世界,那么要么一直忍氣吞聲帮辟,要么韜光養(yǎng)晦直至適應(yīng)速址,沒有別的路可走。 ——《我是貓》

兩個virtual dom樹的比對

1.概述

React通過render方法在內(nèi)存中產(chǎn)生一個樹形virtual dom結(jié)構(gòu)由驹,這個virtual dom結(jié)構(gòu)會被react處理為一個瀏覽器接受的DOM樹芍锚。正是virtual dom的引入,使得react更新性能能夠得到一個較好的表現(xiàn)蔓榄。

當完成了裝載過程的時候闹炉,用戶便擁有機會去引發(fā)界面的更新了。當用戶觸發(fā)了界面更新的時候润樱,此時react仍然通過render方法去生成一個新的virtual dom,注意是生成了整棵virtual dom樹羡棵,而不是引起變化的那部分壹若。那么接下來的操作難道是利用整棵virtual dom樹轉(zhuǎn)化成DOM樹以供瀏覽器重新渲染嗎?在初次掛載過程自然是這樣的。但是其它情況下那就不是這樣了店展,畢竟雖然觸發(fā)了頁面的更新過程养篓,但是引起更新的根源有可能就只是頁面的一小部分,因此在頁面發(fā)生更新時直接拿新生成的virtual dom去生成dom樹是非常不應(yīng)該的赂蕴,至少在性能方面是不能接受的柳弄。

那么React在更新過程又是怎么盡力去避免性能問題的呢?答案就是react會提供一個“調(diào)和”過程概说,這個調(diào)優(yōu)過程會對比新的virtual dom樹以及舊的virtual dom樹碧注,接著找出兩者所不同的地方,根據(jù)不同的地方來修改現(xiàn)有的DOM糖赔。那么這個調(diào)優(yōu)過程判具體又是如何判斷兩棵virtual dom樹是不同的呢萍丐?


2.調(diào)和過程

當React要比較兩棵virtual dom樹的時候,是從根節(jié)點開始遞歸往下對比的放典。在一棵樹中逝变,實際上每個節(jié)點都在某種意義上是某些節(jié)點的根節(jié)點。因此奋构,這個對比算法可以從virtual dom樹上的任何一個節(jié)點開始做比較壳影。對于調(diào)和算法來說,首先他從根節(jié)點的類型開始作比較弥臼。對于這一步宴咧,具有兩個結(jié)果:根節(jié)點的類型是相同的;根節(jié)點的類型是不同的醋火。不同的結(jié)果悠汽,對于調(diào)和算法關(guān)于兩個節(jié)點是否相同會有不同的看法。


3.如果根節(jié)點的類型不同的話

如果根節(jié)點的類型不同的話芥驳,那么react會認為兩個virtual dom樹之間的改變實在是太大了柿冲,會將所有與這個根節(jié)點有關(guān)的子節(jié)點都認為是不同的,因此這些無辜者都會被拋棄兆旬。那么此時將會經(jīng)歷哪些操作呢假抄?答案就是這些舊節(jié)點的卸載以及新節(jié)點的掛載過程,注意對于此時的這種情況來說丽猬,盡管進入的頁面的更新過程宿饱,但是對于react組件來說卻不會進入組件的更新生命周期。

舉個例子:

//before
<div>
  <appHeader />
  <appBody />
  <appFooter />
</div>

//after
<section>
  <appHeader />
  <appBody />
  <appFooter />
</section>

對于上面這個例子來說脚祟,我們將無實質(zhì)性作用的包裹元素由div元素改為了section元素谬以,對于這種情況來說,react會把舊的相關(guān)子節(jié)點(當然也包括根節(jié)點自己)給卸載由桌,接著將新的節(jié)點給掛載到相應(yīng)的位置上去为黎。很顯然的邮丰,這里實質(zhì)性的內(nèi)容appHeader等并沒有發(fā)生變化,但是卻還是被強制卸載掛載了一波铭乾。

那么如何避免上述這種情況呢剪廉?答案是沒有,我們能做就只是避免無意義的更改元素的類型炕檩。難道不能通過設(shè)置shouldComponentUpdate來避免這種情況嗎斗蒋?答案是當然不能,因為這種情況下根本就不會進入組件的更新生命周期啊笛质。


4.如果根節(jié)點的類型相同的話

注意泉沾,但我們比較根節(jié)點的類型的時候是不會比較節(jié)點的屬性的。如果react認為根節(jié)點的類型是相同的話经瓷,那么此時調(diào)和過程將會認為可以重用某些內(nèi)容爆哑,因此不會像上述情況中所提到的大刀闊斧的經(jīng)歷卸載過程以及裝載過程,而是只是對組件進行更新過程舆吮。

具體一點描述的話揭朝,我們知道對于react來說,元素分為兩類:dom元素色冀,組件元素潭袱。當根節(jié)點是dom元素的時候,并且節(jié)點類型相同的話锋恬,那么react會比較dom元素上的屬性以及content屯换,如果發(fā)生變化的話,那么將只會在dom上修改相應(yīng)的部分与学,不會多做某些不必要的更改彤悔。

如果根節(jié)點是組件元素的話,并且節(jié)點類型相同的話索守,那么此時react會比較兩個組件元素上props的不同晕窑,利用新得到的props去更新原來的組件實例,引發(fā)這個組件的更新過程卵佛。

更新過程的生命周期函數(shù)杨赤,我們在前面也提到過,這里在溫習(xí)一下:

  • shouldComponentUpdate
  • componentWillReceiveProps
  • componentWillUpdate
  • render
  • componentDidUpdate

在更新過程中截汪,如果我們的shouldComponentUpdate返回false的話疾牲,那么下面的生命周期函數(shù)就不會得到執(zhí)行了,這些函數(shù)當然包括了那個挺消耗性能的render函數(shù)衙解,因此對于shouldComponentUpdate來說阳柔,他就是掌握了react組件優(yōu)化的生殺大權(quán)(當然,限于調(diào)和過程認為根節(jié)點的類型是相同的情況下)蚓峦。

在這種情況下盔沫,react會接著處理當前根節(jié)點的子節(jié)點医咨,把它們理解為根節(jié)點接著進行同樣的處理。


5.sibling之間發(fā)生變化所引起的調(diào)和處理

在下面這種情況下:

//before
<CommentList>
  <messageItem text="a" />
  <messageItem text="b" />
</CommentList>

//after
<CommentList>
  <messageItem text="c" />
  <messageItem text="a" />
  <messageItem text="b" />
</CommentList>

react處理上面這種情況很出乎意料:他不會認為是新添加了一個text props為“c“的messageItem插入到了第一位架诞,而是認為原有的text props為"a"的messageitem的props被修改為了"c",并且認為原有的text props為"b"的messageitem的props被修改為了"a"干茉,接著新增了一個text props為"b"的messageitem谴忧。

很奇怪吧,而這種做法帶來了這種問題:那就是原有的無辜的sibling都會進行更新過程(畢竟props都發(fā)生了變化能不更新嗎角虫?)沾谓,而新增得sibling那就是必備的掛載過程了。問題是這里造成了不必要的性能浪費戳鹅,畢竟那些其余sibling的內(nèi)容是沒有發(fā)生絲毫變化的均驶。

那么問題來了,如何避免這種情況呢枫虏?答案就是利用react 提供組件的key屬性妇穴,這個key屬性會被react理解為一個react組件的標志,只要你給上述情況的每一個messageItem元素都給添加了一個獨一無二的key屬性的話隶债,那么我們給messageItem組件設(shè)置的shouldComponentUpdate函數(shù)就能夠如期而至的起作用了腾它。


6.需要注意的地方

當使用key屬性的時候,我們必須給他設(shè)置一個獨一無二的值死讹;其次把數(shù)組的每一項的index設(shè)置為key的值也是錯誤的做法瞒滴。


END

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市赞警,隨后出現(xiàn)的幾起案子妓忍,更是在濱河造成了極大的恐慌,老刑警劉巖愧旦,帶你破解...
    沈念sama閱讀 217,826評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件世剖,死亡現(xiàn)場離奇詭異,居然都是意外死亡忘瓦,警方通過查閱死者的電腦和手機搁廓,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,968評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來耕皮,“玉大人境蜕,你說我怎么就攤上這事×柰#” “怎么了粱年?”我有些...
    開封第一講書人閱讀 164,234評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長罚拟。 經(jīng)常有香客問我台诗,道長完箩,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,562評論 1 293
  • 正文 為了忘掉前任拉队,我火速辦了婚禮弊知,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘粱快。我一直安慰自己秩彤,他們只是感情好,可當我...
    茶點故事閱讀 67,611評論 6 392
  • 文/花漫 我一把揭開白布事哭。 她就那樣靜靜地躺著漫雷,像睡著了一般。 火紅的嫁衣襯著肌膚如雪鳍咱。 梳的紋絲不亂的頭發(fā)上降盹,一...
    開封第一講書人閱讀 51,482評論 1 302
  • 那天,我揣著相機與錄音谤辜,去河邊找鬼蓄坏。 笑死,一個胖子當著我的面吹牛每辟,可吹牛的內(nèi)容都是我干的剑辫。 我是一名探鬼主播,決...
    沈念sama閱讀 40,271評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼渠欺,長吁一口氣:“原來是場噩夢啊……” “哼妹蔽!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起挠将,我...
    開封第一講書人閱讀 39,166評論 0 276
  • 序言:老撾萬榮一對情侶失蹤胳岂,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后舔稀,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體乳丰,經(jīng)...
    沈念sama閱讀 45,608評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,814評論 3 336
  • 正文 我和宋清朗相戀三年内贮,在試婚紗的時候發(fā)現(xiàn)自己被綠了产园。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,926評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡夜郁,死狀恐怖什燕,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情竞端,我是刑警寧澤屎即,帶...
    沈念sama閱讀 35,644評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響技俐,放射性物質(zhì)發(fā)生泄漏乘陪。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,249評論 3 329
  • 文/蒙蒙 一雕擂、第九天 我趴在偏房一處隱蔽的房頂上張望啡邑。 院中可真熱鬧,春花似錦捂刺、人聲如沸谣拣。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,866評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至拔鹰,卻和暖如春仪缸,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背列肢。 一陣腳步聲響...
    開封第一講書人閱讀 32,991評論 1 269
  • 我被黑心中介騙來泰國打工恰画, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人瓷马。 一個月前我還...
    沈念sama閱讀 48,063評論 3 370
  • 正文 我出身青樓拴还,卻偏偏與公主長得像,于是被迫代替她去往敵國和親欧聘。 傳聞我的和親對象是個殘疾皇子片林,可洞房花燭夜當晚...
    茶點故事閱讀 44,871評論 2 354

推薦閱讀更多精彩內(nèi)容

  • 深入JSX date:20170412筆記原文其實JSX是React.createElement(componen...
    gaoer1938閱讀 8,064評論 2 35
  • 參考文章:深度剖析:如何實現(xiàn)一個Virtual DOM 算法 作者:戴嘉華React中一個沒人能解釋清楚的問題——...
    waka閱讀 5,965評論 0 21
  • 原教程內(nèi)容詳見精益 React 學(xué)習(xí)指南,這只是我在學(xué)習(xí)過程中的一些閱讀筆記怀骤,個人覺得該教程講解深入淺出费封,比目前大...
    leonaxiong閱讀 2,834評論 1 18
  • 以下內(nèi)容是我在學(xué)習(xí)和研究React時,對React的特性蒋伦、重點和注意事項的提取弓摘、精練和總結(jié),可以做為React特性...
    科研者閱讀 8,232評論 2 21
  • 做為從事互聯(lián)網(wǎng)行業(yè)的產(chǎn)品狗一枚,時刻保持著對世界的好奇心十分重要研叫。所以在新年伊始锤窑,給自己制定了2016年的讀書計劃...
    哲歌閱讀 250評論 0 0