react性能優(yōu)化

React 使用虛擬 DOM,它是在瀏覽器中的 DOM 子樹的渲染描述疙剑,這個平行的描述讓 React 避免創(chuàng)建和操作 DOM 節(jié)點氯迂,這些遠比操作一個 JavaScript 對象慢。當一個組件的 props 或 state 改變核芽,React 會構造一個新的虛擬 DOM 和舊的進行對比來決定真實 DOM 更新的必要性囚戚,只有在它們不相等的時候,React 才會使用盡量少的改動更新 DOM轧简。

在此之上驰坊,React 提供了生命周期函數(shù) shouldComponentUpdate,在重新渲染機制回路(虛擬 DOM 對比和 DOM 更新)之前會被觸發(fā)哮独,賦予開發(fā)者跳過這個過程的能力拳芙。這個函數(shù)默認返回 true察藐,讓 React 執(zhí)行更新。

shouldComponentUpdate: function(nextProps, nextState) {
  return true;
}

一定要記住舟扎,React 會非常頻繁的調用這個函數(shù)分飞,所以要確保它的執(zhí)行速度夠快。

假如你有個帶有多個對話的消息應用睹限,如果只有一個對話發(fā)生改變譬猫,如果我們在 ChatThread 組件執(zhí)行 shouldComponentUpdate,React 可以跳過其他對話的重新渲染步驟羡疗。

shouldComponentUpdate: function(nextProps, nextState) {
  // TODO: return whether or not current chat thread is
  // different to former one.
}

因此染服,總的說,React 通過讓用戶使用 shouldComponentUpdate 減短重新渲染回路叨恨,避免進行昂貴的更新 DOM 子樹的操作柳刮,而且這些必要的更新,需要對比虛擬 DOM痒钝。

shouldComponentUpdate 實戰(zhàn)

這里有個組件的子樹秉颗,每一個都指明了 shouldComponentUpdate 返回值和虛擬 DOM 是否相等,最后送矩,圓圈的顏色表示組件是否需要更新蚕甥。

[圖片上傳失敗...(image-225d59-1530447751850)]

在上面的示例中,因為 C2 的 shouldComponentUpdate 返回 false益愈,React 就不需要生成新的虛擬 DOM梢灭,也就不需要更新 DOM夷家,注意 React 甚至不需要調用 C4 和 C5 的 shouldComponentUpdate蒸其。

C1 和 C3 的 shouldComponentUpdate 返回 true,所以 React 需要向下到葉子節(jié)點檢查它們库快,C6 返回 true摸袁,因為虛擬 DOM 不相等,需要更新 DOM义屏。最后感興趣的是 C8靠汁,對于這個節(jié)點,React 需要計算虛擬 DOM闽铐,但是因為它和舊的相等蝶怔,所以不需要更新 DOM。

注意 React 只需要對 C6 進行 DOM 轉換兄墅,這是必須的踢星。對于 C8,通過虛擬 DOM 的對比確定它是不需要的隙咸,C2 的子樹和 C7沐悦,它們甚至不需要計算虛擬 DOM成洗,因為 shouldComponentUpdate

那么藏否,我們怎么實現(xiàn) shouldComponentUpdate 呢瓶殃?比如說你有一個組件僅僅渲染一個字符串:

React.createClass({
  propTypes: {
    value: React.PropTypes.string.isRequired
  },

  render: function() {
    return <div>{this.props.value}</div>;
  }
});

我們可以簡單的實現(xiàn) shouldComponentUpdate 如下:

shouldComponentUpdate: function(nextProps, nextState) {
  return this.props.value !== nextProps.value;
}

非常好!處理這樣簡單結構的 props/state 很簡單副签,我門甚至可以歸納出一個基于淺對比的實現(xiàn)遥椿,然后把它 Mixin 到組件中。實際上 React 已經提供了這樣的實現(xiàn): PureRenderMixin

但是如果你的組件的 props 或者 state 是可變的數(shù)據(jù)結構呢淆储?比如說修壕,組件接收的 prop 不是一個像 'bar' 這樣的字符串,而是一個包涵字符串的 JavaScript 對象遏考,比如 { foo: 'bar' }:

React.createClass({
  propTypes: {
    value: React.PropTypes.object.isRequired
  },

  render: function() {
    return <div>{this.props.value.foo}</div>;
  }
});

前面的 shouldComponentUpdate 實現(xiàn)就不會一直和我們期望的一樣工作:

// assume this.props.value is { foo: 'bar' }
// assume nextProps.value is { foo: 'bar' },
// but this reference is different to this.props.value
this.props.value !== nextProps.value; // true

這個問題是當 prop 沒有改變的時候 shouldComponentUpdate 也會返回 true慈鸠。為了解決這個問題,我們有了這個替代實現(xiàn):

shouldComponentUpdate: function(nextProps, nextState) {
  return this.props.value.foo !== nextProps.value.foo;
}

基本上灌具,我們結束了使用深度對比來確保改變的正確跟蹤青团,這個方法在性能上的花費是很大的,因為我們需要為每個 model 寫不同的深度對比代碼咖楣。就算這樣督笆,如果我們沒有處理好對象引用,它甚至不能工作诱贿,比如說這個父組件:

React.createClass({
  getInitialState: function() {
    return { value: { foo: 'bar' } };
  },

  onClick: function() {
    var value = this.state.value;
    value.foo += 'bar'; // ANTI-PATTERN!
    this.setState({ value: value });
  },

  render: function() {
    return (
      <div>
        <InnerComponent value={this.state.value} />
        <a onClick={this.onClick}>Click me</a>
      </div>
    );
  }
});

內部組件第一次渲染的時候娃肿,它會獲取 { foo: 'bar' } 作為 value 的值。如果用戶點擊了 a 標簽珠十,父組件的 state 會更新成 { value: { foo: 'barbar' } }料扰,觸發(fā)內部組件的重新渲染過程,內部組件會收到 { foo: 'barbar' } 作為 value 的新的值焙蹭。

這里的問題是因為父組件和內部組件共享同一個對象的引用晒杈,當對象在 onClick 函數(shù)的第二行發(fā)生改變的時候,內部組件的屬性也發(fā)生了改變孔厉,所以當重新渲染過程開始拯钻,shouldComponentUpdate 被調用的時候,this.props.value.foonextProps.value.foo 是相等的撰豺,因為實際上 this.props.valuenextProps.value 是同一個對象的引用粪般。

因此,我們會丟失 prop 的改變污桦,縮短重新渲染過程亩歹,UI 也不會從 'bar' 更新到 'barbar'

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子捆憎,更是在濱河造成了極大的恐慌舅柜,老刑警劉巖,帶你破解...
    沈念sama閱讀 210,835評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件躲惰,死亡現(xiàn)場離奇詭異致份,居然都是意外死亡,警方通過查閱死者的電腦和手機础拨,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,900評論 2 383
  • 文/潘曉璐 我一進店門氮块,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人诡宗,你說我怎么就攤上這事滔蝉。” “怎么了塔沃?”我有些...
    開封第一講書人閱讀 156,481評論 0 345
  • 文/不壞的土叔 我叫張陵蝠引,是天一觀的道長。 經常有香客問我蛀柴,道長螃概,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,303評論 1 282
  • 正文 為了忘掉前任鸽疾,我火速辦了婚禮吊洼,結果婚禮上,老公的妹妹穿的比我還像新娘制肮。我一直安慰自己冒窍,他們只是感情好,可當我...
    茶點故事閱讀 65,375評論 5 384
  • 文/花漫 我一把揭開白布豺鼻。 她就那樣靜靜地躺著综液,像睡著了一般。 火紅的嫁衣襯著肌膚如雪拘领。 梳的紋絲不亂的頭發(fā)上意乓,一...
    開封第一講書人閱讀 49,729評論 1 289
  • 那天樱调,我揣著相機與錄音约素,去河邊找鬼。 笑死笆凌,一個胖子當著我的面吹牛圣猎,可吹牛的內容都是我干的。 我是一名探鬼主播乞而,決...
    沈念sama閱讀 38,877評論 3 404
  • 文/蒼蘭香墨 我猛地睜開眼送悔,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起欠啤,我...
    開封第一講書人閱讀 37,633評論 0 266
  • 序言:老撾萬榮一對情侶失蹤荚藻,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后洁段,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體应狱,經...
    沈念sama閱讀 44,088評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,443評論 2 326
  • 正文 我和宋清朗相戀三年祠丝,在試婚紗的時候發(fā)現(xiàn)自己被綠了疾呻。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,563評論 1 339
  • 序言:一個原本活蹦亂跳的男人離奇死亡写半,死狀恐怖岸蜗,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情叠蝇,我是刑警寧澤璃岳,帶...
    沈念sama閱讀 34,251評論 4 328
  • 正文 年R本政府宣布,位于F島的核電站悔捶,受9級特大地震影響矾睦,放射性物質發(fā)生泄漏。R本人自食惡果不足惜炎功,卻給世界環(huán)境...
    茶點故事閱讀 39,827評論 3 312
  • 文/蒙蒙 一枚冗、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧蛇损,春花似錦赁温、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,712評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至更啄,卻和暖如春稚疹,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背祭务。 一陣腳步聲響...
    開封第一講書人閱讀 31,943評論 1 264
  • 我被黑心中介騙來泰國打工内狗, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人义锥。 一個月前我還...
    沈念sama閱讀 46,240評論 2 360
  • 正文 我出身青樓柳沙,卻偏偏與公主長得像,于是被迫代替她去往敵國和親拌倍。 傳聞我的和親對象是個殘疾皇子赂鲤,可洞房花燭夜當晚...
    茶點故事閱讀 43,435評論 2 348

推薦閱讀更多精彩內容