(閱讀本文約需 2 分鐘)javascript
衆所周知,在使用 Redux 時最麻煩的一個部分就是 reducer 的編寫,因爲 Redux 要求狀態是 immutable 的,也就是說,發生變化的狀態樹必定是一個新的引用。 因此 reducer 常常會寫成這樣:java
function todoApp(state = initialState, action) {
switch (action.type) {
case SET_VISIBILITY_FILTER:
return Object.assign({}, state, {
visibilityFilter: action.filter
})
case ADD_TODO:
return Object.assign({}, state, {
todos: [
...state.todos,
{
text: action.text,
completed: false
}
]
})
default:
return state
}
}
複製代碼
不少人會稱之爲深克隆,其實並非,這個過程既不是深克隆也不是淺克隆。git
首先咱們來談談深克隆是否可行,若是你的 reducer 在每次狀態發生變化時都進行深克隆處理,你的 app 毋庸置疑是能夠 work 的,Time Travelling 固然也能夠用,那麼問題會出在哪裏呢?github
咱們不妨經過圖示來看一下:redux
整個狀態樹被重建了,這就意味着 PureComponent
和 shouldComponentUpdate
沒有實現好的組件都會從新 render。app
因此在實際項目中,咱們引入了 Immutable.js,就是爲了不寫出繁瑣或者不正確的 reducer。相似的還有 immer 這樣的庫。函數
Immutable.js 內部會使用 Shared Structure 來避免深克隆,一方面提高了 Immutable.js 自身的性能,另外一方面能幫助 React 更高效地渲染。就像這樣:性能
當一個對象中的一個鍵發生變化時,這個對象中其餘鍵的值不會有任何變化,而引用該對象的對象會產生一份新的引用,以此類推。這樣,咱們的狀態樹就能夠像值類型同樣進行對比了:優化
節點 4 發生變化,節點 一、2 變化先後必定不相等,可是節點 三、五、6 沒有變化仍然是相等的。咱們甚至不用 deepEquals
,對比引用就能夠了,由於 Immutable.js 能夠保證它們不發生變化。ui
所以,咱們的 React 組件若是採用了 PureComponent,就能自動得到最好的優化,與變化無關的組件也不會從新渲染。
然而在實際使用中,咱們又遇到了問題,即使使用了 Immutable.js,每次更新時仍是有不少無關組件發生更新了。搜查了一遍代碼,我發現咱們如今有不少這樣的寫法:
const mapStateToProps = state => {
const user = selectCurrentUser(state)
const me = user.toJS()
const myTeam = selectMyTeam(state)
const team = myTeam && myTeam.toJS()
//...
return { user, me, myTeam, team /*, ...*/ }
}
複製代碼
問題就出在 toJS
的調用上,根據文檔:
Deeply converts this Keyed collection to equivalent native JavaScript Object.
toJS
會將本來 structure shared 的對象徹底深克隆一遍,全部 PureComponent 又會從新渲染。能夠看一下咱們如今的狀況:
能夠看到,改變了一個與左側邊欄無關的按鈕狀態的時候,左側邊欄依舊從新渲染了。
下面是去掉了 toJS
調用後的狀況:
是否是好多了。
至此咱們也可以得出結論了,React 的渲染性能很大一部分取決於更新的粒度,當咱們的 render 函數已經足夠龐大時,咱們可以作的只有分步更新(Fiber 和 Time Slicing 主要解決的問題)和精準更新了。
而要作到精準更新,就必定要處理好狀態的變化,其實最簡單的方法就是狀態扁平化,對象層級越小,咱們的代碼裏可能出現的問題就越少。另外,儘量將 connect
放置在須要狀態的組件外,目前咱們仍是有不少組件過早 connect
,而後將狀態一層一層經過 props
傳下去,這也是狀態對象層級太深(有多深我就不截圖了...)致使的。Redux 狀態更新(dispatch)時,全部的 Connect(...)
組件都會根據本身的 mapped state 進行更新,越早 connect
的組件越有可能發生更新,而其子組件若是沒有處理好 shouldComponentUpdate
就會出現許多無用的更新,白白損失性能。
References: