一、什麼是Immutable?react
Immutable是一旦建立,就不能被更改的數據。chrome
對Immutable對象的任何修改或添加刪除操做都會返回一個新的Immutable對象。redux
Immutable實現的原理是:Persistent Data Structure(持久化數據結構),數組
也就是數據改變時(增刪改)要保證舊數據同時可用且不變安全
爲了不深拷貝把全部節點都複製一遍帶來的性能損耗,Immutable使用了Structural Sharing(結構共享)性能優化
即若是對象樹節點發生變化,只修改這個結點和受它影響的父節點,其餘節點共享數據結構
二、immutable經常使用APIapp
//Map() 原生object轉Map對象 (只會轉換第一層,注意和fromJS區別) immutable.Map({name:'danny', age:18}) //List() 原生array轉List對象 (只會轉換第一層,注意和fromJS區別) immutable.List([1,2,3,4,5]) //fromJS() 原生js轉immutable對象 (深度轉換,會將內部嵌套的對象和數組所有轉成immutable) immutable.fromJS([1,2,3,4,5]) //將原生array --> List immutable.fromJS({name:'danny', age:18}) //將原生object --> Map //toJS() immutable對象轉原生js (深度轉換,會將內部嵌套的Map和List所有轉換成原生js) immutableData.toJS(); //查看List或者map大小 immutableData.size 或者 immutableData.count() // is() 判斷兩個immutable對象是否相等 immutable.is(imA, imB); //merge() 對象合併 var imA = immutable.fromJS({a:1,b:2}); var imA = immutable.fromJS({c:3}); var imC = imA.merge(imB); console.log(imC.toJS()) //{a:1,b:2,c:3} //增刪改查(全部操做都會返回新的值,不會修改原來值) var immutableData = immutable.fromJS({ a:1, b:2, c:{ d:3 } }); var data1 = immutableData.get('a') // data1 = 1 var data2 = immutableData.getIn(['c', 'd']) // data2 = 3 getIn用於深層結構訪問 var data3 = immutableData.set('a' , 2); // data3中的 a = 2 var data4 = immutableData.setIn(['c', 'd'], 4); //data4中的 d = 4 var data5 = immutableData.update('a',function(x){return x+4}) //data5中的 a = 5 var data6 = immutableData.updateIn(['c', 'd'],function(x){return x+4}) //data6中的 d = 7 var data7 = immutableData.delete('a') //data7中的 a 不存在 var data8 = immutableData.deleteIn(['c', 'd']) //data8中的 d 不存在
三、immutable可讓代碼更簡潔、提升性能、讓redux更快更方便更安全函數
在redux中每一個reducer都返回一個新的對象(數組),經常會看到這樣的代碼:性能
// reducer ... return [ ...oldArr.slice(0,3), newValue, ...oldArr.slice(4) ];
爲了返回新的對象(數組),不得不有上面奇怪的樣子,
而在使用更深的數據結構時會變的更棘手。
讓咱們看看Immutable的作法:
// reducer ... return oldArr.set(4, newValue);
Immutable使用了Structure Sharing會盡可能複用內存,
甚至之前使用的對象也能夠再次被複用,
未引用的對象會被垃圾回收。
四、immutable使用過程當中的一些注意點
a、fromJS和toJS會深度轉換數據,隨之帶來的開銷較大,儘量避免使用,單層數據轉換使用Map()和List() b、js是弱類型,但Map類型的key必須是string!(也就是咱們取值是要用get('1')而不是get(1)) c、全部針對immutable變量的增刪改必須左邊有賦值,由於全部操做都不會改變原來的值,只是生成一個新的變量 d、獲取深層深套對象的值時不須要作每一層級的判空(JS中若是不判空會報錯,immutable中只會給undefined) e、immutable對象直接能夠轉JSON.stringify(),不須要顯式手動調用toJS()轉原生 f、判斷對象是不是空能夠直接用size g、調試過程當中要看一個immutable變量中真實的值,能夠chrome中加斷點,在console中使用.toJS()方法來查看
五、在react中使用immutable
react作性能優化時,可使用shouldComponentUpdate(),
由於不管子組件用沒用到父組件的參數只要父組件從新渲染了,
子組件就會從新渲染
而shouldComponentUpdate()很好地幫咱們解決了這個問題,
//在render函數調用前判斷:若是先後state中Number不變,經過return false阻止render調用 shouldComponentUpdate(nextProps,nextState){ if(nextState.Number == this.state.Number){ return false } }
經過這個鉤子咱們能夠很巧妙地避免了不少組件的從新渲染這種浪費性能的行爲。
可是這個鉤子默認返回true,也就是說它默認是都從新渲染的,
那麼就須要屢次使用,
而咱們在使用原生屬性的時候,爲了得出是true仍是false
不得不使用deepCopy、deepCompare,
而這兩種方法很是消耗性能
而在有了Immutable以後,
Immutable 則提供了簡潔高效的判斷數據是否變化的方法,
來減小 React 重複渲染,提升性能,只需 ===
和 is
比較就能知道是否須要執行 render()
,
而這個操做幾乎 0 成本,因此能夠極大提升性能。
修改後的 shouldComponentUpdate
是這樣的:
import { is } from 'immutable'; shouldComponentUpdate: (nextProps = {}, nextState = {}) => { const thisProps = this.props || {}, thisState = this.state || {}; //判斷長度是否改變,長度改變的話,數據必定改必定須要從新渲染 if (Object.keys(thisProps).length !== Object.keys(nextProps).length || Object.keys(thisState).length !== Object.keys(nextState).length) { return true; } //當原數據和next的數據長度一致時須要遍歷循環比較 for (const key in nextProps) { if (!is(thisProps[key], nextProps[key])) { return true; } } for (const key in nextState) { if (thisState[key] !== nextState[key] || !is(thisState[key], nextState[key])) { return true; } } return false; }
六、redux中應用immutable的小demo
a、個人demo大概是這樣
b、store文件夾下的index.js中引入immutable
import { createStore, applyMiddleware } from 'redux' //引入immutable import Immutable from 'immutable' import thunk from 'redux-thunk' import reducers from './reducers' //變爲map函數 const initialState = Immutable.Map(); //注入 const store = createStore(reducers, initialState, applyMiddleware(thunk)) export default store
c、store文件夾下的reducers.js中修改聯合reducer的方法
//之前引入的是redux的combineReducers方法 // import { combineReducers } from 'redux' //如今改成引入redux-immutable中 import { combineReducers } from 'redux-immutable' import { reducer as cookbook } from 'pages/cookbook' import { reducer as menu } from 'pages/menu' export default combineReducers({ cookbook, menu })
d、在actionType.js和actionCreator.js中無變化,還是定義變量和獲取數據
e、在頁面的reducer.js中
import { CHANGE_FROM } from './actionTypes' //引入fromJS import { fromJS } from 'immutable' //把獲取到的數據變成immutable的Map()形式 const defaultState = fromJS({ from: 'category' }) //對state進行改變的時候採用immutable的方法 export default (state=defaultState, action) => { if (action.type === CHANGE_FROM) { return state.set('from', action.from) } return state }
f、在頁面使用的時候轉化成原生使用
//經過toJS()方法轉換爲原生再進行map遍歷 let data = this.props.categories && this.props.categories.get('熱門').toJS().slice(0, 11).map((value, index) => { return { icon: value.img, text: value.title } })
以上。