项目中一直在使用Immutable js,最开始使用Immutable js,我的内心是抗拒的,简单的对象操作整得那么复杂,有必要吗。随着后面开发对数据操作非常频繁的项目后,渐渐发现了Immutable的闪光点; 现在就总结下使用Immutable时的一些体会;
一:什么是 Immutable Data
Immutable Data 就是一旦创建,就不能再被更改的数据。对 Immutable 对象的任何修改或添加删除操作都会返回一个新的 Immutable 对象。 Immutable 实现的原理是 Persistent Data >Structure(持久化数据结构),也就是使用旧数据创建新数据时,要保证旧数据同时可用且不变。同时为了避免 deepCopy 把所有节点都复制一遍带来的性能损耗,Immutable 使用了 Structural Sharing(结构共享),即如果对象树中一个节点发生变化,只修改这个节点和受它影响的父节点,其它节点则进行共享;
通过阅读源码,知道,Immutable js 支持Map、List、Set、Collection、OrderedMap、Stack、OrderedSet、Record、Range、Repeat九种数据结构;
最重要的也是经常用到的就是前三个;
Map:键值对集合,对应于 Object,ES6 也有专门的 Map 对象
List:有序可重复的列表,对应于 Array
Set:无序且不可重复的列表
二:为什么要用Immutable,Immutable带来的好处有哪些?
第一点,在没有使用Immutable之前操作store对象型数据的时候在不想修改原数据的时候通常的做法是复制一份,在复制的数据上做更新修改操作;但是每次deep-copy都要把整个对象递归的复制一份,如果遇到很复杂的对象型数据时,这样性能会很差;
而现在使用了Immutable,当我们发生一个set操作的时候,Immutable.js只修改这个节点和受它影响的父节点,其它节点则进行共享,可以大大提高性能;这里在网上找到一张动图:
第二点, 在react中的shouldComponentUpdate方法中,判断组件是否需要更新的时候;对于复杂的对象型数据,去对比两个对象的值是否相等是很麻烦的;而Immutable提供了 is方法,去判断两个Immutable对象值是否相等;
第三点,由于Immutable提供了丰富的API,对于操作复杂的数据结构也变得很直观和方便;
粗略阅读源码,探究Immutable数据结构如何设计 Map对象为例子:
let obj1 = Immutable.Map({
aa: {
'a':1
},
bb: {
'b':2
}
});
console.log(obj1)
上面的a Immutable对象打印出来是这样的:
可以看到,多层次的对象转为Immutable的Map对象时,只有第一层会被转为Immutable对象。
想要每个每层都转为Immutable对象,用fromJS:
let obj2 = Immutable.fromJS({
aa: {
'a':1
},
bb: {
'b':2
}
});
console.log(obj2);
截图如下:
Immutable在处理Map对象时,会将需要转成Map对象的普通对象的值放 在_root的entries对象下;这个普通对象的每个子项会是一个数组,每个子项的key是数组的第0项,value是数组的第1项; 这样存储的好处是当使用 set或者get 方法设置或取对象时,就去迭代entries对象,设置或找到相应的值。
Immutable对象使用频率最高的莫过于setIn和getIn方法; setIn和getIn方法第一个参数都是需要查询的层级路径,路径用数组的格式;
setIn有第二个参数,第二个参数是需要设置的值;
以setIn为例,如:
obj2.setIn(['aa', 'a'], 2),
Immutable会去从最顶层entries对象开始迭代; 查找最外层的entries对象,暂且管它叫entries0,先看entries0的第0项是不是“aa”; 如果entries0的第0项不是“aa”, 就会给entrie0中新增加一项;
比如:
obj2.setIn(['cc', 'c'], 3);
此时entries0第0项不是‘cc’, 所以会给entries0新增 ‘cc’ 对象;
如果是"aa", 就会再查询entries0的第1项;entries0的第1项在这里也是Map对象,内部也有一个entries对象,暂且叫entries1; 这里查找entrie1第0项是不是‘a’,发现是‘a’, 就给 这个‘a’赋值为2;如果不是“a”, 同理,会给entries1新增一项;下面来看getIn操作
如果getIn的第一参数提供的层级路径在Immutable对象中找不到,会返回什么呢?
如:
let obj3 = obj2.getIn(['dd', 'd']);
console.log(obj3) // undefined
结果返回 undefined, 这个很好理解,因为在第一个参数对应的路径下找不到想要的key,所以返回undefined;
如果此时还有第二个参数呢,结果会怎样?
let obj3 = obj2.getIn(['dd', 'd'], 5);
console.log(obj3) // 5
结果返回5;结果证明,当使用getIn时,第一个参数无效时,结果直接返回第二个参数;(getIn方法传第二个参数没有意义);
在最开始使用Immutable时,需要学习新的API,查看一些文档,需要花费一些时间; 还需要适应新的对象操作方式,从传统的obj.a, obj[a], 转换到obj.get('a'), obj.get(['a', 'b', 'c']) 等等,但是真的投入的去使用后,Immutable会给你意想不到惊喜。