react項(xiàng)目?jī)?yōu)化

前言

傳統(tǒng)的Diff算法O(N3),React Diff基于三大前提將復(fù)雜度降為O(N)

1.tree diff十嘿,跨層dom操作比較少吨悍,結(jié)構(gòu)不同直接銷毀
2.component diff婉烟,相同類型節(jié)點(diǎn)有相同樹形結(jié)構(gòu)砌些,只做更新,不同類型直接銷毀
3.element diff朵耕,同層子元素根據(jù)唯一標(biāo)識(shí)做區(qū)分

一.代碼優(yōu)化

1.key

設(shè)置唯一穩(wěn)定的key
render() {
  return (
    <div>
      this.state.user.map(item => <div key={item.id}>{item.name}</div>)
    </div>  
  )
}

2.節(jié)點(diǎn)內(nèi)容

節(jié)點(diǎn)類型分兩大類炫隶,一類是DOM元素類型(如div,span),另一類是React組件

Dom元素比較屬性和內(nèi)容
<div style={{color: 'red'}} onClick={() =>this.handleClick()}>變化前</div>
...
// 變化后屬性和內(nèi)容都變化了
<div style={{color: 'red'}} onClick={() =>this.handleClick()}>變化后</div>

注意引用類型的比較

 {color: 'red'} === {color: 'red'}  // false
 var fun1 = () => {}
 var fun2 = () => {}
 fun1 === fun2  // false

優(yōu)化后阎曹,屬性指向都不變

const style = {color: 'red'}
...
constructor(props) {
  super(props)
  this.handleClick = this.handleClick.bind(this)
}
...
render() {
  return {
    <div style={style} onClick={this.handleClick}>更改后</div>
  }
}

redux版本優(yōu)化前伪阶,

<TodoItem
  key={item.id}
  onRemove={() => onRemoveTodo(item.id)}
  ...

...

redux版本優(yōu)化后

// 方法1
<TodoItem
  key={item.id}
  onRemove={onRemoveTodo}
  id={item.id}
  ...
>
...

const mapDispatchToProps = (dispatch,ownProps) => ({
  onRemoveTodo: ownProps.onRemoveTodo(ownProps.id)),
})

// 方法2
<TodoItem
  key={item.id}
  id={item.id}
  ...
>
...
const mapDispatchToProps = (dispatch,ownProps) => ({
  onRemoveTodo: () => dispatch(onRemoveTodo(ownProps.id)),
})

3.shouldComponentUpdate(nextProps,nextState)

為了避免浪費(fèi)多余的渲染,return false可以阻止組件的更新
shouldComponentUpdate(nextProps,nextState) {
  return nextProps.isChange !== this.props.isChange   // true or false
}

PureComponent類內(nèi)部也是用了shouldComponentUpdate

// PureComponent內(nèi)部
if (this._compositeType === CompositeTypes.PureClass) {
  shouldUpdate = !shallowEqual(prevProps, nextProps)
  || !shallowEqual(inst.state, nextStat e);
}
return shouldUpdate;

shallowEqual(淺比較)芬膝,只做簡(jiǎn)單類型的判斷望门。

state = {
  arr = [1,2,3,4,5]
}
shouldComponentUpdate(nextProps,nextState) {
  return nextState.arr !== this.state.nextState // false
}
handleClick = () => {
 const { arr } = this.state
  arr.push(6)
    this.setState({
      arr,
  })
}

render () {
  ...
}

解決方法deepEqual,但如果變量嵌套深會(huì)對(duì)性能有消耗

二.工具使用

1.immutable(一種不可更改的數(shù)據(jù))

網(wǎng)上一般寫法

import { is } from 'immutable' 
...
  shouldComponentUpdate = (nextProps = {} , nextState = {}) => {
    if(Object.keys(this.props).length !== Object.keys(nextProps).length ||
        Object.keys(this.state).length !== Object.keys(nextState).length
    ) {
      return true
    }
    for(const key in nextProps) {
      if(this.props[key] !== nextProps[key] || !is(this.props[key],nextProps[key])) {
        return true
      }
    }
    for(const key in nextState) {
      if(this.state[key] !== nextState[key] || !is(this.state[key],nextState[key])) {
        return true
      }
    }
    return false
  }
...

我項(xiàng)目中優(yōu)化前后對(duì)比


  shouldComponentUpdate = (nextProps = {} , nextState = {}) => {
    if(!is(nextState,this.state)) {
      return true
    }
    return false
  }
優(yōu)化前.png

優(yōu)化后.png

使用了結(jié)構(gòu)共享锰霜,避免deepCopy筹误,可節(jié)省內(nèi)存

2.reselect

一種選擇器中間件,認(rèn)為輸入?yún)?shù)state相同癣缅,就沒必要進(jìn)行計(jì)算厨剪,直接抽取以往的值

const mapState = (state)=>({
  todos:state.todos,
  filter:state.filter,
  visibleTodos:getVisibleTodos(state.todos,state.filter)
});
//selector.js
export const todosSelector = (state) => state.todos;
export const filterSelector = (state) => state.filter;
export const visibleTodosSelector = createSelector(
  [todosSelector,filterSelector],
  (todos,filter)=>{return getVisibleTodos(todos,filter)}  //這里假設(shè)已經(jīng)定義一個(gè)getVisibleTodos函數(shù)用來返回要顯示哪些todo項(xiàng)
 );
 
//container.js
import {visibleTodosSelector}
const mapState = (state)=>({
  todos:visibleTodosSelector(state)
});

3.其他

少用{...props}
適當(dāng)拆分組件
壓縮哄酝,合并,commonChunksPulgin(webpack4里面移除了commonChunksPulgin插件祷膳,放在了config.optimization里面)

css預(yù)處理語言寫法陶衅,防止多余編譯

三.Fiber架構(gòu)遷移

以往的react渲染是一氣呵成,不能打斷直晨,同步渲染計(jì)算大的話容易阻塞UI線程搀军。
react16后提出Fiber,使react從Stack reconciler轉(zhuǎn)變?yōu)閒iber reconciler


Fiber兩個(gè)Phase.png

將渲染分割成多個(gè)事務(wù)勇皇,使得以往的棧結(jié)構(gòu)可以定制優(yōu)先級(jí)罩句,暫停,復(fù)用敛摘,其中第一個(gè)階段是可以隨時(shí)被打斷的階段门烂,這使得某部分舊的生命周期函數(shù)造成不安全的危害
componentWillMount
componentWillReceiveProps
componentWillUpdate
用其它生命周期函數(shù)再結(jié)合兩個(gè)新的生命周期函數(shù),足以替代它們的業(yè)務(wù)場(chǎng)景

getDerivedStateFromProps(nextProps, prevState)

  static getDerivedStateFromProps(nextProps, prevState) {
    if (nextProps.selectCodeId !== prevState.codeId) {
      return {
        codeId: nextProps.selectCodeId,
        first: false,
      };
    }
    return null;
  }

getDerivedStateFromProps沒有附加渲染的情況下更新狀態(tài)的唯一方法兄淫,可以用來替代componentWillReceiveProps

getSnapshotBeforeUpdate(prevProps, prevState)

export default class ScrollingList extends Component {
  constructor(props) {
    super(props)
    this.scrollRef = null
  }

  getSnapshotBeforeUpdate(prevProps, prevState) {
    if (prevProps.list.length < this.props.list.length) {
      return (
        this.scrollRef.scrollHeight - this.scrollRef.scrollTop
      );
    }
    return null;
  }

  componentDidUpdate(prevProps, prevState, snapshot) {
    if (snapshot !== null) {
      this.scrollRef.scrollTop =
        this.scrollRef.scrollHeight - snapshot;
    }
  }

  render() {
    return (
      ....
    );
  }


}
react16.png
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末屯远,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子捕虽,更是在濱河造成了極大的恐慌慨丐,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,039評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件薯鳍,死亡現(xiàn)場(chǎng)離奇詭異咖气,居然都是意外死亡挨措,警方通過查閱死者的電腦和手機(jī)挖滤,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,426評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來浅役,“玉大人斩松,你說我怎么就攤上這事【跫龋” “怎么了惧盹?”我有些...
    開封第一講書人閱讀 165,417評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)瞪讼。 經(jīng)常有香客問我钧椰,道長(zhǎng),這世上最難降的妖魔是什么符欠? 我笑而不...
    開封第一講書人閱讀 58,868評(píng)論 1 295
  • 正文 為了忘掉前任嫡霞,我火速辦了婚禮,結(jié)果婚禮上希柿,老公的妹妹穿的比我還像新娘诊沪。我一直安慰自己养筒,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,892評(píng)論 6 392
  • 文/花漫 我一把揭開白布端姚。 她就那樣靜靜地躺著晕粪,像睡著了一般。 火紅的嫁衣襯著肌膚如雪渐裸。 梳的紋絲不亂的頭發(fā)上巫湘,一...
    開封第一講書人閱讀 51,692評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音昏鹃,去河邊找鬼剩膘。 笑死,一個(gè)胖子當(dāng)著我的面吹牛盆顾,可吹牛的內(nèi)容都是我干的怠褐。 我是一名探鬼主播,決...
    沈念sama閱讀 40,416評(píng)論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼您宪,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼奈懒!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起宪巨,我...
    開封第一講書人閱讀 39,326評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤磷杏,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后捏卓,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體极祸,經(jīng)...
    沈念sama閱讀 45,782評(píng)論 1 316
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,957評(píng)論 3 337
  • 正文 我和宋清朗相戀三年怠晴,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了遥金。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,102評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡蒜田,死狀恐怖稿械,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情冲粤,我是刑警寧澤美莫,帶...
    沈念sama閱讀 35,790評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站梯捕,受9級(jí)特大地震影響厢呵,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜傀顾,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,442評(píng)論 3 331
  • 文/蒙蒙 一襟铭、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦蝌矛、人聲如沸道批。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,996評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)隆豹。三九已至,卻和暖如春茅逮,著一層夾襖步出監(jiān)牢的瞬間璃赡,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,113評(píng)論 1 272
  • 我被黑心中介騙來泰國(guó)打工献雅, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留碉考,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,332評(píng)論 3 373
  • 正文 我出身青樓挺身,卻偏偏與公主長(zhǎng)得像侯谁,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子章钾,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,044評(píng)論 2 355

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