一、什么是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 } })
以上。