React性能優(yōu)化(1)

React是一個(gè)專注UI層的框架驮审,它使用虛擬DOM技術(shù),以保證它UI的告訴喧染耻卡;使用單向數(shù)據(jù)流疯汁,因此它的數(shù)據(jù)綁定更加的簡(jiǎn)單;那么它內(nèi)部是如何保持簡(jiǎn)單高效的UI渲染呢劲赠?這種渲染機(jī)制有可能存在什么性能問(wèn)題呢涛目?

React組件渲染問(wèn)題引出

React不直接操作DOM,它在內(nèi)存中維護(hù)一個(gè)快速相應(yīng)的DOM描述凛澎,render方法返回一個(gè)DOM的描述霹肝,React能夠計(jì)算出兩個(gè)DOM描述的差異,然后更新瀏覽器中的DOM塑煎。這就是著名的DOM Diff.

也就是說(shuō)React在接受屬性(props)或者狀態(tài)(state)更新時(shí)沫换,就會(huì)通過(guò)前面的方式更新UI。所以React整個(gè)UI喧染是比較快的最铁,但是這里面可能出現(xiàn)的問(wèn)題是:
假設(shè)我們定義一個(gè)父組件讯赏,其包含了5000個(gè)子組件。我們有一個(gè)輸入框輸入操作冷尉,每次輸入一個(gè)數(shù)字漱挎,對(duì)應(yīng)的那個(gè)子組件的背景變紅。

<Components>
  <Components-1 />
  <Components-2 />
  <Components-3 />
  ...
  <Components-5000 />
</Components>

這樣我們?cè)谳斎霐?shù)字1雀哨,則子組件1的背景色變化磕谅,但是在這個(gè)過(guò)程中私爷,所有的子組件都進(jìn)行了重新渲染,導(dǎo)致整體渲染變慢膊夹,造成這種現(xiàn)象的原因是React中父組件更新默認(rèn)出發(fā)所有子組件更新衬浑。
同時(shí),我們經(jīng)常在便利列表元素的時(shí)候會(huì)遇到這樣的提示:

Warning: Each child in an array or iterator should have a unique "key" prop.

這就是我們要探討的兩個(gè)性能優(yōu)化點(diǎn):

  • 1.父組件更新默認(rèn)觸發(fā)子組件更新
  • 2.列表類(lèi)型的組件默認(rèn)更新方式非常復(fù)雜

React性能檢測(cè)工具

我們利用react-addons-pref進(jìn)行性能檢測(cè)放刨。引入的方式如下:

import Perf from 'react-addons-perf'
window.Perf = Perf // 掛載到全局變量方便使用

檢測(cè)方法工秩,在瀏覽器控制臺(tái)中輸入如下命令:

  • 開(kāi)始記錄:Perf.start()
  • 結(jié)束記錄:Perf.stop()
  • 打印結(jié)果:printInclusive()
1491542236024.png

控制臺(tái)會(huì)以表格的形式展示出結(jié)果:

1491542600197.png

上圖記錄了每個(gè)組件的執(zhí)行耗時(shí),渲染次數(shù)等關(guān)鍵信息进统。我們可以有針對(duì)性的進(jìn)行優(yōu)化助币。

注意:生產(chǎn)環(huán)境不要引入Perf

React性能優(yōu)化原理

這是React官網(wǎng)對(duì)組件渲染機(jī)制的描述圖,其中綠色組件代表不需要更新麻昼,紅色組件需要更新奠支,影響更新的條件主要有SCU(shouldComponentUpdate)及DOM DIff結(jié)果。

1491537026005.png

我們?cè)賮?lái)看看 組件觸發(fā)更新的流程圖:

render.png

通過(guò)上述的流程圖抚芦,再對(duì)比喧染的圖解可以看到倍谜,React的性能瓶頸主要出現(xiàn)在DOM以及DOM Diff的過(guò)程。如果進(jìn)行性能優(yōu)化叉抡,關(guān)鍵在于:

  • shouldComponentUpdata 階段判斷尔崔,如果屬性及狀態(tài)與上一次相同,這個(gè)時(shí)候很明顯UI不會(huì)變化褥民,也不需要執(zhí)行后續(xù)生成DOM季春,DOM Diff的過(guò)程了,可以提高性能消返。
  • DOM Diff 階段優(yōu)化载弄,提高Diff的效率

如何提高組件的渲染效率

針對(duì)文章開(kāi)頭提出的兩個(gè)性能問(wèn)題,我們得到以下解決方案:

  • 子組件執(zhí)行 shouldComponentUpdate 方法撵颊,自行決定是否更新
  • 給列表中的組件添加key屬性

我們可以控制子組件的shouldComponentUpdate從而控制是否渲染:

shouldComponentUpdate(nextProps, nextState) {
    // 如果當(dāng)前的value值與待更新不相等宇攻,才執(zhí)行更新
    return this.props.value !== nextProps.value;
}

針對(duì)列表遍歷類(lèi)型,遍歷的時(shí)候添加唯一key屬性倡勇,對(duì)子組件進(jìn)行唯一識(shí)別逞刷,準(zhǔn)確知道要操作的子組件,提高DOM Diff的效率妻熊。

array.map(val, index) => {
   return <span key={index}>{val}</span>
})

PureRenderMixin與PureComponent

為了提高React組件喧染性能夸浅,React針對(duì)組件的shouldComponentUpdate方法進(jìn)行了封裝處理,我們不需要在每個(gè)組件里面手動(dòng)編寫(xiě)shouldComponentUpdate扔役。

PureRenderMixin

React在之前版本提供了 PureRenderMixin 的mixin形式帆喇,其用法如下:

// react官方demo
import PureRenderMixin from 'react-addons-pure-render-mixin';
class FooComponent extends React.Component {
  constructor(props) {
    super(props);
    this.shouldComponentUpdate = PureRenderMixin.shouldComponentUpdate.bind(this);
  }

其原理就是重寫(xiě)了 shouldComponentUpdate 方法。

PureComponent

React 15.3.0 新增了一個(gè) PureComponent 類(lèi)亿胸,以 ES2015 class 的方式方便地定義純組件 (pure component)坯钦,用于取代之前的 PureRenderMixin法严。

這個(gè)類(lèi)的用法很簡(jiǎn)單,如果你有些組件是純組件葫笼,那么把繼承類(lèi)從 Component 換成 PureComponent 即可。當(dāng)組件更新時(shí)拗馒,如果組件的 props 和 state 都沒(méi)發(fā)生改變路星,render 方法就不會(huì)觸發(fā),省去 Virtual DOM 的生成和比對(duì)過(guò)程诱桂,達(dá)到提升性能的目的洋丐。

import React, { PureComponent } from 'react'

class Example extends PureComponent {
  render() {
    // ...
  }
}

這里要注意的是:PureRenderMixin、PureComponent 內(nèi)進(jìn)行的僅僅是淺比較對(duì)象(shallowCompare)挥等。如果對(duì)象包含了復(fù)雜的數(shù)據(jù)結(jié)構(gòu)友绝,深層次的差異可能會(huì)產(chǎn)生誤判。比如肝劲,如果我們的state變?yōu)椋?/p>

state = {
     value: { foo: 'bar' }
}

// 每次更改value值的時(shí)候進(jìn)行:
this.setState({ value: newValue });

此時(shí)直接通過(guò)值的比較是行不通的迁客,因?yàn)閷?duì)象的引用關(guān)系,導(dǎo)致在子組件里面接受到的 this.props.value 與 nextProps.value 永遠(yuǎn)都是相等的辞槐。這里的解決方案主要有:

  • 深比較: 原理與深拷貝類(lèi)似掷漱,比較耗時(shí),不推薦
  • immutable.js:FaceBook官方提出的不可變數(shù)據(jù)解決方案榄檬,主要解決了復(fù)雜數(shù)據(jù)在deepClone和對(duì)比過(guò)程中性能損耗

總結(jié)

雖然React提供了Virtual DOM DOM Diff 等優(yōu)秀的能力來(lái)提高渲染性能卜范,但是在實(shí)際使用過(guò)程中,我們經(jīng)常會(huì)遇到父組件更新鹿榜,不需要更新所以子組件的場(chǎng)景(分頁(yè))海雪,此時(shí)必須考慮利用React本周的渲染機(jī)制來(lái)進(jìn)行優(yōu)化。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末舱殿,一起剝皮案震驚了整個(gè)濱河市奥裸,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌怀薛,老刑警劉巖刺彩,帶你破解...
    沈念sama閱讀 217,406評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異枝恋,居然都是意外死亡创倔,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,732評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門(mén)焚碌,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)畦攘,“玉大人,你說(shuō)我怎么就攤上這事十电≈海” “怎么了叹螟?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,711評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)台盯。 經(jīng)常有香客問(wèn)我罢绽,道長(zhǎng),這世上最難降的妖魔是什么静盅? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,380評(píng)論 1 293
  • 正文 為了忘掉前任良价,我火速辦了婚禮,結(jié)果婚禮上蒿叠,老公的妹妹穿的比我還像新娘明垢。我一直安慰自己,他們只是感情好市咽,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,432評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布痊银。 她就那樣靜靜地躺著,像睡著了一般施绎。 火紅的嫁衣襯著肌膚如雪溯革。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,301評(píng)論 1 301
  • 那天粘姜,我揣著相機(jī)與錄音鬓照,去河邊找鬼。 笑死孤紧,一個(gè)胖子當(dāng)著我的面吹牛豺裆,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播号显,決...
    沈念sama閱讀 40,145評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼臭猜,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了押蚤?” 一聲冷哼從身側(cè)響起蔑歌,我...
    開(kāi)封第一講書(shū)人閱讀 39,008評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎揽碘,沒(méi)想到半個(gè)月后次屠,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,443評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡雳刺,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,649評(píng)論 3 334
  • 正文 我和宋清朗相戀三年劫灶,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片掖桦。...
    茶點(diǎn)故事閱讀 39,795評(píng)論 1 347
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡本昏,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出枪汪,到底是詐尸還是另有隱情涌穆,我是刑警寧澤怔昨,帶...
    沈念sama閱讀 35,501評(píng)論 5 345
  • 正文 年R本政府宣布,位于F島的核電站宿稀,受9級(jí)特大地震影響趁舀,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜祝沸,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,119評(píng)論 3 328
  • 文/蒙蒙 一赫编、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧奋隶,春花似錦、人聲如沸悦荒。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,731評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)搬味。三九已至境氢,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間碰纬,已是汗流浹背萍聊。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,865評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留悦析,地道東北人寿桨。 一個(gè)月前我還...
    沈念sama閱讀 47,899評(píng)論 2 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像强戴,于是被迫代替她去往敵國(guó)和親亭螟。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,724評(píng)論 2 354

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

  • 3. JSX JSX是對(duì)JavaScript語(yǔ)言的一個(gè)擴(kuò)展語(yǔ)法骑歹, 用于生產(chǎn)React“元素”预烙,建議在描述UI的時(shí)候...
    pixels閱讀 2,824評(píng)論 0 24
  • React對(duì)重排和重繪的提高雅虎性能優(yōu)化比較重要的點(diǎn),老司機(jī)自行忽略道媚。如下圖扁掸,HTML被瀏覽器解析為DOM樹(shù),CS...
    HT_Jonson閱讀 1,659評(píng)論 0 49
  • 原教程內(nèi)容詳見(jiàn)精益 React 學(xué)習(xí)指南最域,這只是我在學(xué)習(xí)過(guò)程中的一些閱讀筆記谴分,個(gè)人覺(jué)得該教程講解深入淺出,比目前大...
    leonaxiong閱讀 2,834評(píng)論 1 18
  • 說(shuō)在前面 關(guān)于 react 的總結(jié)過(guò)去半年就一直碎碎念著要搞起來(lái)羡宙,各(wo)種(tai)原(lan)因(le)狸剃。心...
    陳嘻嘻啊閱讀 6,867評(píng)論 7 41
  • 周六上午休息,早早地起了床狗热,因?yàn)榻裉煲憷蠇岓w檢钞馁,搭著對(duì)面婆娘的車(chē)到了醫(yī)院虑省。我去食堂買(mǎi)了包子、茶葉蛋僧凰,匆匆趕往門(mén)診...
    qqy1閱讀 240評(píng)論 3 3