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';
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 的原因。