如何有效地提高react渲染效率--深複製,淺複製,immutable原理
1. 效能意義:保持state不變這個約束引導我們使用區域性更新物件的方法,這樣會可以非常有效地提高react或其他顯示框架的渲染效率。我們先來看看為了保持資料不變性,要怎麼對state做更新,以我們的蘋果籃子state為例:
例子:通知開始摘蘋果:apple/BEGIN_PICK_APPLE
為了保證每個版本的state不變性,我們有兩種實現方式:“深複製”,“淺複製”。我們來看兩種模式的內部原理:
深複製方式:有人會這樣想:“保持state的不變性很容易,只需要深複製一個state, 讓後在新的state要怎麼修改就怎麼修改,不ok了嗎?”,如下就是深複製
這種方式是一種很低階保持不變性的方式:
-
深複製操作執行效率低
-
沒有為渲染環節提供提高渲染效率的鋪墊
它只是簡單迎合保持資料不變性的約束,雖然有一定除錯意義,但是,不但沒有提高程式的效能,反而降低了程式的總體效能!沒有實踐意義。
淺複製方式:淺複製模式只對內部資料發生變化的引用做更新,如下
“state” 物件的內部資料發生變化,所以建立新的state引用;而apples array 內部資料不發生變化,所以就不對該引用做更新!在這個操作中,這種淺複製的方法執行效率比較高,而且其簡單地實現了資料不變性,為除錯帶來方便,同時,也是更重要的,這種淺複製的方式極大地提高了視覺元件渲染階段的執行效率!我們來對比一下:當用戶點選摘蘋果時,如果使用“深複製”,渲染程式需要重新遍歷整個state物件樹來做視覺更新,而使用淺複製來實現資料不變性時,渲染程式只需要遍歷state物件的一級子節點
備註:在react元件裡面,要開啟條件更新這個生命週期函式 shouldComponentUpdate, 才會對把這個效能提高點釋放出來,類似這樣:
...
shouldComponentUpdate(nextProps) {
return nextProps.state != this.props.state;
}
...
下面我們再給出 “吃蘋果” reducer 進行淺複製的例子:
現在大家應該理解了用淺複製實現資料不變性的原理和意義了,下面我們來看具體的程式碼實現。
我們的程式碼用 es6 編寫,這裡要用到 es6 兩個非常方便的特性:
大家可以稍微看一下文件,或者看我下面的例子就知道其用法了:
// apple basket reducer
export default (state = {
isPicking: false,
newAppleId: 1,
apples: [
{
id: 0,
weight: 235,
isEaten: false
}
]
}, action) => {
let newState ;
switch (action.type) {
case 'apple/BEGIN_PICK_APPLE':
newState = Object.assign({}, state, {
isPicking: true
});
return newState;
case 'apple/DONE_PICK_APPLE':
newState = Object.assign({}, state, {
apples: [
...state.apples,
{
id: state.newAppleId,
weight: action.payload,
isEaten: false
}
],
newAppleId: state.newAppleId + 1,
isPicking: false
})
return newState;
case 'apple/FAIL_PICK_APPLE':
//這裡只是簡單處理
newState = Object.assign({}, state, {
isPicking: false
});
return newState;
case 'apple/EAT_APPLE':
newState = Object.assign({}, state, {
apples: [
...state.apples.slice(0, action.payload),
Object.assign({}, state.apples[action.payload], { isEaten: true }),
...state.apples.slice(action.payload + 1)
]
})
return newState;
default:
return state;
}
};
大家會發現這種淺複製操作在開發的過程會複雜一點,於是就有了 immutable.js 這個專門處理不變性資料的庫(也是facebook出品),它可以使用類似賦值的方式生成淺複製的不變性資料,下面來看看它怎麼簡化我們的開發:
我們用 apple/EAT_APPLE
這個reducer來說明。
這是原來的 reducer:
...
case 'apple/EAT_APPLE':
newState = Object.assign({}, state, {
apples: [
...state.apples.slice(0, action.payload),
Object.assign({}, state.apples[action.payload], { isEaten: true }),
...state.apples.slice(action.payload + 1)
]
})
return newState;
...
這是使用 immutable.js 庫的reducer :
import { fromJS } from 'immutable';
...
case 'apple/EAT_APPLE':
return fromJS(state).setIn(['apples',action.payload,'isEaten'], true).toJS();
...
用了immutable.js後,輕鬆一行程式碼搞定!如果團隊約定 state 都用 immutable 內部的資料型別,就可以連 fromJS 和 toJS 的轉化都省了,超級方便!
到這裡, reducer 任務的介紹就結束啦~