react優化性能的幾點

1. 基本原理

1.1 render()函數

一般來說,要盡可能少地在 render 函數中做操作。如果非要做一些復雜操作或者計算,也許你可以考慮使用一個 memoized 函數以便于緩存那些重復的結果。可以看看 Lodash.memoize,這是一個開箱即用的記憶函數。

反過來講,避免在組件的 state 上存儲一些容易計算的值也很重要。舉個例子,如果 props 同時包含 firstName 和 lastName,沒必要在 state 上存一個 fullName,因為它可以很容易通過提供的 props 來獲取。如果一個值可以通過簡單的字符串拼接或基本的算數運算從 props 派生出來,那么沒理由將這些值包含在組件的 state 上。

1.2 Prop 和 state

重要的是要記住,只要 props(或 state)的值不等于之前的值,React 就會觸發重新渲染。如果 props 或者 state 包含一個對象或者數組,嵌套值中的改變會觸發重新渲染。考慮到這一點,你需要注意在每次渲染的生命周期中,創建一個新的 props 或者 state 都可能無意中導致了性能下降。(注:對象或者數組只要引用不變,是不會觸發rerender的)

舉幾個現在我在開發中遇到的幾個問題:

例子: 函數綁定的問題

/*
給 prop 傳入一個行內綁定的函數(包括 ES6 箭頭函數)實質上是在每次父組件 render 時傳入一個新的函數。
*/
render() {
  return (
    <div>
      <a onClick={() => this.onRender()}>顯示</a>
      <a onClick={() => this.onRender.bind(this)}>顯示</a>
    </div>
  );
}


/*
應該在構造函數中處理函數綁定并且將已經綁定好的函數作為 prop 的值
*/

constructor( props ) {
  this.doSomething = this.doSomething.bind( this );
  //or
  this.doSomething = (...args) => this.doSomething(...args);
}
render() {
  return (
    <div>
       <a onClick={ this.onRender }>顯示</a>
    </div>
  );
}

例子: 對象或數組字面量

/*
對象或者數組字面量在功能上來看是調用了 Object.create() 和 new Array()。這意味如果給 prop 傳遞了對象字面量或者數組字面量。每次render 時 React 會將他們作為一個新的值。這在處理 行內樣式時通常是有問題的。
*/

/*遇到的寫法 */
// 每次渲染時都會為 style 新建一個對象字面量
render() {
  return <div style={ { backgroundColor: 'red' } }/>
}

/* 建議寫法 */
// 在組件外聲明
const style = { backgroundColor: 'red' };

render() {
  return <div style={ style }/>
}

例子 : 注意兜底值字面量

/*
有時我們會在 render 函數中創建一個兜底的值來避免 undefined 報錯。在這些情況下,最好在組件外創建一個兜底的常量而不是創建一個新的字面量。
/*
/* 遇到的寫法 */
render() {
  let thingys = [];
  // 如果 this.props.thingys 沒有被定義,一個新的數組字面量會被創建
  if( this.props.thingys ) {
    thingys = this.props.thingys;
  }

  return <ThingyHandler thingys={ thingys }/>
}

/* 遇到的寫法 */
render() {
  // 這在功能上和前一個例子一樣
  return <ThingyHandler thingys={ this.props.thingys || [] }/>
}

/* 建議的寫法*/

// 在組件外部聲明
const NO_THINGYS = [];

render() {
  return <ThingyHandler thingys={ this.props.thingys || NO_THINGYS }/>
}

1.3 盡可能的保持 Props(和 State)簡單和精簡

理想情況下,傳遞給組件的 props 應該是它直接需要的。為了將值傳給子組件而將一個大的、復雜的對象或者很多獨立的 props 傳遞給一個組件會導致很多不必要的組件渲染(并且會增加開發復雜性)。

我們使用 Redux 作為狀態容器,所以在我們看來,最理想的是方案在組件層次結構的每一個層級中使用 react-reduxconnect() 函數直接從 store 上獲取數據。connect 函數的性能很好,并且使用它的開銷也非常小。

這里已經優化了。

1.4 組件方法

由于組件方法是為組件的每個實例創建的,如果可能的話,使用 helper/util 模塊的純函數或者靜態類方法。尤其在渲染大量組件的應用中會有明顯的區別。

2. 組件是否重新渲染

視圖的變化是邪惡的

2.1 shouldComponentUpdate()

React 有一個生命周期函數 shouldComponentUpdate()。這個方法可以根據當前的和下一次的 props 和 state 來通知這個 React 組件是否應該被重新渲染。

然而使用這個方法有一個問題,開發者必須考慮到需要觸發重新渲染的每一種情況。這會導致邏輯復雜,一般來說,會非常痛苦。如果非常需要,你可以使用一個自定義的shouldComponentUpdate()
方法,但是很多情況下有更好的選擇。

2.2 React.PureComponent

React 從 v15 開始會包含一個 PureComponent 類,它可以被用來構建組件。React.PureComponent聲明了它自己的 shouldComponentUpdate() 方法,它自動對當前的和下一次的 props 和 state 做一次淺對比。有關淺對比的更多信息,請參考這個 Stack Overflow:http://stackoverflow.com/questions/36084515/how-does-shallow-compare-work-in-react

在大多數情況下,React.PureComponent 是比 React.Component更好的選擇。在創建新組件時,首先嘗試將其構建為純組件,只有組件的功能需要時才使用 React.Component。更多信息,請查閱相關文檔 React.PureComponent

2.3 怎么使用PureComponent

PureComponent節約了我們的時間,避免了多余的代碼。那么,掌握如何正確使用它是非常重要的,否則如果使用不當,它就無法發揮作用。因為PureComponent僅僅是淺比較(shadow comparison),所以改變組件內部的 props 或者 state ,它將不會發揮作用。所以建議按上面建議的幾種寫法來開發。這樣如果用上面建議的寫法來寫的話就有兩個好處:

  • 一是避免組件每次渲染都做重復的事
  • 二是避免在使用PureComponent時損失PureComponent的優點

注意:
使用了pureComponent之后,只是淺比較,這樣會導致改變組件內部的props之后不會更新視圖,所以pureComponent建議用在純UI類的組件里。

正常情況下,遷移的方式非常簡單,就像改變組件繼承的基類,從

class MyComponent extends Component {...}

class MyComponent extends PureComponent {...}

在PureComponent出現之前,通常是自己實現一個這樣的組件:

import React,{ Component }from 'react'
import shallowEqual from 'react-pure-render/shallowEqual'

export default class PureComponent extends Component{
  shouldComponentUpdate(nextProps, nextState) {
    return !shallowEqual(this.props, nextProps) || !shallowEqual(this.state, nextState)
  }
}

當然react性能優化不僅僅有上面提到的幾點,還可以從webpack,使用immutable.js上優化

tuiguang.png
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,362評論 6 544
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,577評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,486評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,852評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,600評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,944評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,944評論 3 447
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,108評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,652評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,385評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,616評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,111評論 5 364
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,798評論 3 350
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,205評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,537評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,334評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,570評論 2 379

推薦閱讀更多精彩內容

  • 作為一個合格的開發者,不要只滿足于編寫了可以運行的代碼。而要了解代碼背后的工作原理;不要只滿足于自己的程序...
    六個周閱讀 8,481評論 1 33
  • 說在前面 關于 react 的總結過去半年就一直碎碎念著要搞起來,各(wo)種(tai)原(lan)因(le)。心...
    陳嘻嘻啊閱讀 6,893評論 7 41
  • 3. JSX JSX是對JavaScript語言的一個擴展語法, 用于生產React“元素”,建議在描述UI的時候...
    pixels閱讀 2,872評論 0 24
  • 深入JSX date:20170412筆記原文其實JSX是React.createElement(componen...
    gaoer1938閱讀 8,093評論 2 35
  • 40、React 什么是React?React 是一個用于構建用戶界面的框架(采用的是MVC模式):集中處理VIE...
    萌妹撒閱讀 1,036評論 0 1