react 性能優(yōu)化

webpack階段

路由階段

1 .路由懶加載
2 .React.lazy 目前只支持默認導出(default exports)

import React, { Suspense, lazy } from 'react';
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';

const Home = lazy(() => import('./routes/Home'));
const About = lazy(() => import('./routes/About'));

const App = () => (
  <Router>
    <Suspense fallback={<div>Loading...</div>}>
//必須搭配這個東西
      <Switch>
        <Route exact path="/" component={Home}/>
        <Route path="/about" component={About}/>
      </Switch>
    </Suspense>
  </Router>
);

顆镣⒚唬化控制可控組件

1 .可控組件的DOM值收到react數(shù)據(jù)的state控制,一旦react的state控制數(shù)據(jù)狀態(tài)發(fā)生變化,觸發(fā)了render,如果父組件內(nèi)容比較簡單,還好,但是如果父組件復(fù)雜,還會影響到子組件,那就非常的復(fù)雜了
1.1 .但由于協(xié)調(diào)算法的優(yōu)化, 觸發(fā)render不一定觸發(fā)真實dom的更新

2 .舉例

1 .父組件啟動了一個定時器,這就是每一秒都會更新整個自己的組件,如果這個里面調(diào)用了別的組件,那么別的組件也是會一起觸發(fā)更新的.
2 .他的思路是這樣,把setState的單獨做成最小的粒度組件,不然他有子組件,這樣自然就不會影響到別的
3 .目前這里兄弟組件之間是不會互相影響的.

3 .只要父組件的render了承疲,那么默認情況下就會觸發(fā)子組件的render過程往核,子組件的render過程又會觸發(fā)它的子組件的render過程谤狡,一直到React元素(即jsx中的<div>這樣的元素.并不是只有傳入props才會導致變化

4 .shouldComponentUpdate

shouldComponentUpdate(nextProps, nextState) {
  return nextState.someData !== this.state.someData
}
state里的數(shù)據(jù)這么多,還有對象何吝,還有復(fù)雜類型數(shù)據(jù)忆某,react的理念就是拆分拆分再拆分,這么多子組件奏路,我要每個組件都去自己一個一個對比嗎畴椰??不存在的鸽粉,這么麻煩斜脂,要知道我們的終極目標是不勞而獲-_-
//hook里面也不能用
//弊端:如果存在很多子孫組件的話,會有很大的工作量,并且可能會漏加

5 .React.PureComponent

無論組件是否是 PureComponent,如果定義了 shouldComponentUpdate()触机,那么會調(diào)用它并以它的執(zhí)行結(jié)果來判斷是否 update帚戳。在組件未定義 shouldComponentUpdate() 的情況下,會判斷該組件是否是 PureComponent威兜,如果是的話销斟,會對新舊 props、state 進行 shallowEqual 比較椒舵,一旦新舊不一致蚂踊,會觸發(fā) update。

*   淺判等 只會比較到兩個對象的 ownProperty 是否符合 [源碼](https://github.com/facebook/fbjs/blob/c69904a511b900266935168223063dd8772dfc40/packages/fbjs/src/core/shallowEqual.js#L39)

盡量將復(fù)雜類型數(shù)據(jù)(ArrayList)所關(guān)聯(lián)的視圖單獨拆成PureComonent有助于提高渲染性能笔宿,比如表單犁钟、文本域和復(fù)雜列表在同一個 render() 中,表單域的輸入字段改變會頻繁地觸發(fā) setState() 從而導致 組件 重新 render()泼橘。而用于渲染復(fù)雜列表的數(shù)據(jù)其實并沒有變化涝动,但由于重新觸發(fā) render(),列表還是會重新渲染炬灭。

//目前來看
import React from 'react';
import { immutableRenderDecorator } from 'react-immutable-render-mixin';

@immutableRenderDecorator
class Test extends React.Component {
  render() {
    return <div></div>;
  }
}

hook下面

1 .父子組件完全沒有任何依賴,父組件不需要傳給子組件數(shù)據(jù)和方法

對于函數(shù)組件來說并沒有這個生命周期可以調(diào)用醋粟,因此想實現(xiàn)性能優(yōu)化只能通過 React.memo(<Component />) 來做,這種做法和繼承 PureComponent 的原理一致重归。

React.memo 包一下
import React from 'react'
export default React.memo(()=>{
    console.log('TopSon 更新')
    return (
        <div>
            h11
        </div>
    )
})
//算一個外部優(yōu)化,這樣甚至都不會觸發(fā)子組件的任何邏輯,不會執(zhí)行任何業(yè)務(wù)邏輯

2 .父組件傳值給子組件,這個需要內(nèi)外搭配,內(nèi)部就是useMemo()

const TopSon=useMemo(()=>{
        console.log('111')
        return topSon
    },[])
//不能通過這樣生成一個新的帶有緩存的組件,他的操作只能起作用在有state變化的.只會執(zhí)行部分邏輯.

1 .對于變化的值,這里添加依賴

建立獨立的請求渲染單元

1 .把頁面,分為請求數(shù)據(jù)戰(zhàn)術(shù)部分和基礎(chǔ)部分,不需要請求數(shù)據(jù),直接寫好的.
2 .對于一些邏輯不是很復(fù)雜的數(shù)據(jù)展示部分,可以使用一種獨立組件,獨立請求數(shù)據(jù),獨立控制渲染的模式.這樣一個數(shù)據(jù)請求發(fā)生的重現(xiàn)渲染,也不會影響其他的組件.每一部分抽取出來米愿,形成獨立的渲染單元,每個組件都獨立數(shù)據(jù)請求到獨立渲染
3 .雖然react有很好的diff算法去協(xié)調(diào)相同的節(jié)點,但是長列表,或者說直接不渲染是最好的結(jié)果了
4 .無論從class聲明,還是fun聲明的無狀態(tài),都有一套自身的優(yōu)化機制,shouldComponentUpdate還是hook中的useMemo,useCallback,都可以根據(jù)自身情況,定制一個符合場景的渲染條件,使得依賴數(shù)據(jù)請求組件形成一個自己的,小的渲染,環(huán)境,可以根據(jù)這個小的環(huán)境自定義優(yōu)化機制.

immetable.js

1 .提高對象的比較性能,pureComponent只能對對象進行淺比較,對于對象的數(shù)據(jù)類型,無法比較
2 .這里是如何和hook里面結(jié)合起來的呢,hook的useMemo依賴的都是后面的數(shù)據(jù),無法拿到前后的數(shù)據(jù)啊,或者說每一個數(shù)據(jù)難道還要保存兩份版本,然后專門用這個函數(shù)diff嗎?
3 .本質(zhì)就是useMemo的第二個參數(shù)能否diff到object的變化.

寫法上的小細節(jié)

1 .綁定事件不要寫箭頭函數(shù),因為箭頭函數(shù)每次渲染都會創(chuàng)建一個新的事件處理器,導致子組件每次都會重新渲染
2 .循環(huán)正確使用key

1 .不要使用index做key,每次還是會全部diff
2 .不要用index拼接其他字段:如果有元素移動或者刪除,失去對應(yīng)關(guān)系之后,剩下的節(jié)點都不能得到有效的復(fù)用
3 .一定要使用唯一的id來做key

3 .基本上所有的函數(shù)都用useMemo,useCallback包起來做緩存

//一次操作3個click函數(shù)
 const [ handerClick1 , handerClick2  ,handerClick3] = useMemo(()=>{
        const fn1 = ()=>{
            /* 一些操作 */
        }
        const fn2 = ()=>{
            /* 一些操作 */
        }
        const  fn3= ()=>{
            /* 一些操作 */
        }
        return [fn1 , fn2 ,fn3]
    },[]) 

避免重復(fù)渲染

1 .學會使用批量更新

1 .批量更新失敗的場景 異步場景之后操作多個state
handerClick=()=>{
    setTimeout(() => {
        this.setState({ a:a+1 })
        this.setState({ b:b+1 })
        this.setState({ c:c+1 })
    }, 0)
}

 const handerClick = () => {
    Promise.resolve().then(()=>{
    setB( { ...b } ) 
    setC( c+1 ) 
    setA( a+1 )
    })
}
//常見場景舉例,一次ajax請求之后,獲得多個state,想要通過多個useState改變狀態(tài),會造成多次渲染頁面,為了解決這個問題,我們可以手動批量更新
 const handerClick = () => {
    Promise.resolve().then(()=>{
        unstable_batchedUpdates(()=>{
            setB( { ...b } ) 
            setC( c+1 ) 
            setA( a+1 )
        })
    })
}
react-dom 中提供了unstable_batchedUpdates方法進行手動批量更新鼻吮。這個api更契合react-hooks育苟,

2 .合并state
3 .useMemo,React,memo隔離渲染單元

<button onClick={ ()=> setNumber(number + 1) } >點擊</button>
           <ul>
               {
                useMemo(()=>(list.map(item=>{
                    console.log(1111)
                    return <li key={ item.id }  >{ item.name }</li>
                })),[ list ])
               }
           </ul>
        { useMemo(()=> <ChildrenComponent />,[]) }

//竟然可以寫在這個里面

4 .不是所有的值都需要用state

class Demo extends React.Component{
    text = 111
    componentDidMount(){
        const { a } = this.props
        /* 數(shù)據(jù)直接保存在text上 */
        this.text = a
    }
    render(){
        /* 沒有引入text */
       return <div>{'hello,world'}</div>
    }
}

//壓根渲染沒用到的地方就不需要

5 .useCallBack :useCallback就是 callback加了一個memoize。我們接著往下看

海量數(shù)據(jù)優(yōu)化,時間分片,虛擬列表

1 .時間分片的概念椎木,就是一次性渲染大量數(shù)據(jù)违柏,初始化的時候會出現(xiàn)卡頓等現(xiàn)象博烂。我們必須要明白的一個道理,js執(zhí)行永遠要比dom渲染快的多漱竖。 禽篱,所以對于大量的數(shù)據(jù),一次性渲染闲孤,容易造成卡頓谆级,卡死的情況
2 .,就是用setTimeout把任務(wù)分割讼积,分成若干次來渲染肥照。一共40000個數(shù)據(jù),我們可以每次渲染100個勤众, 分次400渲染舆绎。
3 .setTimeout 可以用 window.requestAnimationFrame() 代替,會有更好的渲染效果们颜。 我們demo使用列表做的吕朵,實際對于列表來說,最佳方案是虛擬列表窥突,

失效原因

1 .在react的event handler內(nèi)部多次setState會被batch為一次更新
2 .但是在一個異步的事件循環(huán)里面多次setState,react不會batch
3 .可以使用React.unstable_batchedUpdates來強制batch

 fn3 = () => {
    // 模擬一個異步操作努溃,真實業(yè)務(wù)里面可能是網(wǎng)絡(luò)請求等
    setTimeout(
      unstable_batchedUpdates(() => {
        this.setState({ a: Math.random() });
        this.setState({ a: Math.random() });
      }),
      0
    );
  };

4 .原因 簡單來解釋,React 的更新是基于 Transaction(事務(wù))的阻问,Transacation 就是給目標執(zhí)行的函數(shù)包裹一下梧税,加上前置和后置的 hook (有點類似 koa 的 middleware),在開始執(zhí)行之前先執(zhí)行 initialize hook称近,結(jié)束之后再執(zhí)行 close hook第队,這樣搭配上 isBatchingUpdates 這樣的布爾標志位就可以實現(xiàn)一整個函數(shù)調(diào)用棧內(nèi)的多次 setState 全部入 pending 隊列,結(jié)束后統(tǒng)一 apply 了刨秆。
但是 setTimeout 這樣的方法執(zhí)行是脫離了事務(wù)的凳谦,react 管控不到,所以就沒法 batch 了衡未。

5 .vue實現(xiàn):是因為 vue 采用了 nexttick 的方式尸执,利用 EventLoop,將一個同步事件循環(huán)過程中所有修改合并缓醋,它本質(zhì)上屬于延遲的批量更新

6 .如果有用react-redux可以用batch代替unstable_batchedUpdates剔交。batch底層還是調(diào)用的unstable_batchedUpdates。

import { batch } from 'react-redux';


55ca5ca65bb745d0d3944a360d4c79e4.png

useMemo,.useCallback

1 .如果傳給子組件的派生狀態(tài)或函數(shù)改衩,每次都是新的引用,那么 PureComponent 和 React.memo 優(yōu)化就會失效驯镊。所以需要使用 useMemo 和 useCallback 來生成穩(wěn)定值葫督,并結(jié)合 PureComponent 或 React.memo 避免子組件重新 Render

發(fā)布者訂閱者模式跳過中間組件的渲染階段

1 .公共數(shù)據(jù)放在reedux等庫上面,只在用到的時候在取,而不需要經(jīng)過不用他的中間組件傳遞值來浪費資源,也就是中間的組件沒有用到數(shù)據(jù),只是起了一個傳遞的作用,這里是不需要的

防抖函數(shù)

懶加載

懶渲染

1 .懶渲染指當組件進入或即將進入可視區(qū)域時才渲染組件竭鞍。常見的組件 Modal/Drawer 等,當 visible 屬性為 true 時才渲染組件內(nèi)容橄镜,也可以認為是懶渲染的一種實現(xiàn)
2 .中判斷組件是否出現(xiàn)在可視區(qū)域內(nèi)是通過 react-visibility-observer[30]

動畫庫直接修改DOM,跳過組件Render

1 .react-spring的動畫實現(xiàn)偎快,當一個動畫啟動后,每次動畫屬性改變不會引起組件重新 Render 洽胶,而是直接修改了 dom 上相關(guān)屬性值
2 .

避免在DIdMount,didUpdate中更新State

1 .所有提交階段的鉤子里面不要進行setState
2 .在提交階段更新組件的State,會再次觸發(fā)組件的更新流程,造成兩倍耗時.一般有以下場景

1 .計算并更新組件的派生狀態(tài).
2 .useLayoutEffect()函數(shù)

React Profiler定位render過程瓶頸

1 .開啟「記錄組件更新原因」
2 .點擊面板上的齒輪晒夹,然后勾選「Record why each component rendered while profiling.」,如下圖姊氓。
3 .然后點擊面板中的虛擬 DOM 節(jié)點丐怯,右側(cè)便會展示該組件重新 Render 的原因。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末翔横,一起剝皮案震驚了整個濱河市读跷,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌禾唁,老刑警劉巖效览,帶你破解...
    沈念sama閱讀 212,884評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異荡短,居然都是意外死亡丐枉,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,755評論 3 385
  • 文/潘曉璐 我一進店門掘托,熙熙樓的掌柜王于貴愁眉苦臉地迎上來瘦锹,“玉大人,你說我怎么就攤上這事烫映≌颖荆” “怎么了?”我有些...
    開封第一講書人閱讀 158,369評論 0 348
  • 文/不壞的土叔 我叫張陵锭沟,是天一觀的道長抽兆。 經(jīng)常有香客問我,道長族淮,這世上最難降的妖魔是什么辫红? 我笑而不...
    開封第一講書人閱讀 56,799評論 1 285
  • 正文 為了忘掉前任,我火速辦了婚禮祝辣,結(jié)果婚禮上贴妻,老公的妹妹穿的比我還像新娘。我一直安慰自己蝙斜,他們只是感情好名惩,可當我...
    茶點故事閱讀 65,910評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著孕荠,像睡著了一般娩鹉。 火紅的嫁衣襯著肌膚如雪攻谁。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 50,096評論 1 291
  • 那天弯予,我揣著相機與錄音戚宦,去河邊找鬼。 笑死锈嫩,一個胖子當著我的面吹牛受楼,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播呼寸,決...
    沈念sama閱讀 39,159評論 3 411
  • 文/蒼蘭香墨 我猛地睜開眼艳汽,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了等舔?” 一聲冷哼從身側(cè)響起骚灸,我...
    開封第一講書人閱讀 37,917評論 0 268
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎慌植,沒想到半個月后甚牲,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,360評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡蝶柿,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,673評論 2 327
  • 正文 我和宋清朗相戀三年丈钙,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片交汤。...
    茶點故事閱讀 38,814評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡雏赦,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出芙扎,到底是詐尸還是另有隱情星岗,我是刑警寧澤,帶...
    沈念sama閱讀 34,509評論 4 334
  • 正文 年R本政府宣布戒洼,位于F島的核電站俏橘,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏圈浇。R本人自食惡果不足惜寥掐,卻給世界環(huán)境...
    茶點故事閱讀 40,156評論 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望磷蜀。 院中可真熱鬧召耘,春花似錦、人聲如沸褐隆。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,882評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至轨蛤,卻和暖如春蜜宪,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背祥山。 一陣腳步聲響...
    開封第一講書人閱讀 32,123評論 1 267
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留掉伏,地道東北人缝呕。 一個月前我還...
    沈念sama閱讀 46,641評論 2 362
  • 正文 我出身青樓,卻偏偏與公主長得像斧散,于是被迫代替她去往敵國和親供常。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,728評論 2 351

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

  • tips:歡迎關(guān)注我在github的博客點擊查看 鸡捐。 使用React.pureComponent栈暇,React.me...
    aermin閱讀 1,293評論 0 1
  • 今天寫一篇關(guān)于react性能優(yōu)化的文章啦。我們知道react有個痛點箍镜,就是會頻繁觸發(fā)不必要的render渲染源祈,只要...
    虎牙工務(wù)員劉波閱讀 377評論 0 0
  • 平常在完成業(yè)務(wù)功能開發(fā)后,不知道你是否還會精益求精色迂,做一些性能優(yōu)化方面的工作呢香缺?React框架中有一些性能優(yōu)化相關(guān)...
    SCQ000閱讀 1,355評論 0 52
  • react 組件更新分為兩個階段:render 階段和 commit 階段;因此可以從兩個方面來優(yōu)化組件性能: 阻...
    wfield閱讀 569評論 0 0
  • react性能優(yōu)化措施有: 1.路由的懶加載歇僧,減少首屏渲染時間: 如果不對路由進行懶加載图张,那么,加載App...
    XiaoMai閱讀 244評論 0 0