整理于互联网
一、前言
从问题说起:熟悉 React
组件生命周期的话都知道:调用 setState
方法总是会触发 render
方法从而进行 vdom re-render
相关逻辑,哪怕实际上你没有更改到 Component.state
1 2 this .state = {count : 0 }this .setState({count : 0 });
为了避免这种性能上的浪费,React
提供了一个 shouldComponentUpdate
来控制触发 vdom re-render
逻辑的条件。于是 PureRenderMixin
作为一种优化技巧被使用。它仅仅是浅比较对象,深层次的数据结构根本不管用
js中的Immutable Data
在javascript
中我们可以通过deep clone
来模拟Immutable Data
,就是每次对数据进行操作,新对数据进行deep clone
出一个新数据
deep clone
当然你或许意识到了,这样非常的慢
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 'use strict' ; var cloneDeep = require ('lodash.clonedeep' );var data = { id: 'data' , author: { name: 'mdemo' , github: 'https://github.com/demohi' } }; var data1 = cloneDeep(data);console .log('equal:' , data1===data); data1.id = 'data1' ; data1.author.name = 'demohi' ; console .log(data.id);console .log(data1.id);console .log(data.author.name);console .log(data1.author.name);
这时候 immutableJS 就派得上用场了
1 2 3 var map1 = Immutable.fromJS({a :1 , b :1 , c :{b :{c :{d :{e :7 }}}}});var map2 = Immutable.fromJS({a :1 , b :1 , c :{b :{c :{d :{e :7 }}}}});Immutable.is(map1, map2);
1 Immutable.fromJS({a :1 , b :2 , c :3 }).map(function (value, key ) { });
二、什么是 Immutable Data
Immutable Data
就是一旦创建,就不能再被更改的数据。对 Immutable
对象的任何修改或添加删除操作都会返回一个新的 Immutable
对象
Immutable
实现的原理是 Persistent Data Structure
(持久化数据结构),也就是使用旧数据创建新数据时,要保证旧数据同时可用且不变
同时为了避免 deepCopy
把所有节点都复制一遍带来的性能损耗,Immutable
使用了 Structural Sharing····
(结构共享),即如果对象树中一个节点发生变化,只修改这个节点和受它影响的父节点,其它节点则进行共享。
打印immutableJS
看看有什么东西
image.png
一个说明不可变的例子
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 let a1 = { b: 1 , c: { c1: 123 } }; let b1 = a1;b1.b = 2 ; console .log(a1.b, b1.b); console .log(a1 === b1); console .log(a1.c === b1.c); let a2 = Immutable.fromJS({ b: 1 , c: { c1: 123 } }); let b2 = a2.set('b' , 2 );console .log(a2.get('b' ), b2.get('b' )); console .log(a2 === b2); console .log(a2.get('c' ) === b2.get('c' ));
三、为什么要在React.js中使用Immutable
它是一个完全独立的库,无论基于什么框架都可以用它。意义在于它弥补了Javascript
没有不可变数据结构的问题
由于是不可变的,可以放心的对对象进行任意操作。在React
开发中,频繁操作state对象或是store
,配合immutableJS
快、安全、方便
熟悉React.js
的都应该知道,React.js
是一个UI = f(states)
的框架,为了解决更新的问题,React.js
使用了virtual dom
,virtual dom
通过diff
修改dom
,来实现高效的dom
更新。
但是有一个问题。当state
更新时,如果数据没变,你也会去做virtual dom
的diff
,这就产生了浪费。这种情况其实很常见
当然你可能会说,你可以使用PureRenderMixin
来解决呀,PureRenderMixin
是个好东西,我们可以用它来解决一部分的上述问题
但PureRenderMixin
只是简单的浅比较,不使用于多层比较。那怎么办?自己去做复杂比较的话,性能又会非常差
方案就是使用immutable.js
可以解决这个问题。因为每一次state
更新只要有数据改变,那么PureRenderMixin
可以立刻判断出数据改变,可以大大提升性能
Immutable 优点
Immutable 降低了 Mutable 带来的复杂度
可变(Mutable
)数据耦合了 Time
和 Value
的概念,造成了数据很难被回溯
Immutable.js
使用了 Structure Sharing
会尽量复用内存,甚至以前使用的对象也可以再次被复用。没有被引用的对象会被垃圾回收
1 2 3 4 5 6 7 8 9 import { Map } from 'immutable' ;let a = Map ({ select: 'users' , filter: Map ({ name : 'Cam' }) }) let b = a.set('select' , 'people' );a === b; a.get('filter' ) === b.get('filter' );
Undo/Redo,Copy/Paste,甚至时间旅行这些功能做起来小菜一碟
因为每次数据都是不一样的,只要把这些数据放到一个数组里储存起来,想回退到哪里就拿出对应数据即可,很容易开发出撤销重做这种功能。
传统的并发非常难做,因为要处理各种数据不一致问题,因此『聪明人』发明了各种锁来解决。但使用了 Immutable
之后,数据天生是不可变的,并发锁就不需要了。
Immutable
本身就是函数式编程中的概念,纯函数式编程比面向对象更适用于前端开发。因为只要输入一致,输出必然一致,这样开发的组件更易于调试和组装。
Immutable 缺点
需要学习新的 API
增加了资源文件大小
容易与原生对象混淆
四、Immutable 的几种数据类型
List
: 有序索引集,类似JavaScript
中的Array
。
Map
: 无序索引集,类似JavaScript
中的Object
。
OrderedMap
: 有序的Map
,根据数据的set()
进行排序。
Set
: 没有重复值的集合。
OrderedSet
: 有序的Set
,根据数据的add
进行排序。
Stack
: 有序集合,支持使用unshift()
和shift()
添加和删除。
Range()
: 返回一个Seq.Indexed
类型的集合,这个方法有三个参数,start
表示开始值,默认值为0
,end
表示结束值,默认为无穷大,step
代表每次增大的数值,默认为1
.如果start = end
,则返回空集合。
Repeat()
: 返回一个vSeq.Indexe
类型的集合,这个方法有两个参数,value
代表需要重复的值,times
代表要重复的次数,默认为无穷大。
Record
: 一个用于生成Record
实例的类。类似于JavaScript
的Object
,但是只接收特定字符串为key
,具有默认值。
Seq
: 序列,但是可能不能由具体的数据结构支持。
Collection
: 是构建所有数据结构的基类,不可以直接构建
上面那么多常用的也就是 List
和Map
五、几个重要的API 1、fromJS()
fromJS()
是最最最常用的将原生JS
数据转换为ImmutableJS
数据的转换方法。使用方式类似于 JSON.parse()
,接收两个参数:json
数据和 reviver
函数
在不传递reviver
函数的情况下,默认将原生JS
的Array
转为List
,Object
转为Map
1 2 3 4 5 6 7 8 9 10 11 12 13 14 const t1 = Immutable.fromJS({a : {b : [10 , 20 , 30 ]}, c : 40 });console .log(t1);const t2 = Immutable.fromJS({a : {b : [10 , 20 , 30 ]}, c : 40 }, function (key, value ) { const isIndexed = Immutable.Iterable.isIndexed(value); return isIndexed ? value.toList() : value.toOrderedMap(); }); console .log(t2);
2、is()
先来看官网的一段话: immutable
数据应该被当作值而不是对象,值是表示该事件在特定时刻的状态。这个原则对理解不可变数据的适当使用是最重要的。为了将Immutable.js
数据视为值,就必须使用Immutable.is()
函数或.equals()
方法来确定值相等,而不是确定对象引用标识的 ===
操作符
所以is()
就是用来对两个immutable
对象进行值比较的。使用方式类似于 Object.is(obj1, obj2)
,接收两个参数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 const map1 = Immutable.Map({a :1 , b :1 , c :1 });const map2 = Immutable.Map({a :1 , b :1 , c :1 });console .log(map1 === map2); console .log(Immutable.is(map1, map2)); console .log(Immutable.is(undefined , undefined )); console .log(Immutable.is(null , undefined )); console .log(Immutable.is(null , null )); console .log(Immutable.is(NaN , NaN )); console .log(Object .is(0 , -0 ) ,Immutable.is(-0 , 0 ));
3、Map
Map
数据类型,对应原生 Object
数组。最最常用的 数据结构之一,循环时无序(orderedMap
有序),对象的 key
可以是任意值。具体看下面的例子
1 2 3 4 console .log(Map ().set(List.of(1 ), 'list-of-one' ).get(List.of(1 )));console .log(Map ().set(NaN , 'NaN' ).get(NaN ));console .log(Map ().set(undefined , 'undefined' ).get(undefined ));console .log(Map ().set(null , 'null' ).get(null ));
OrderedMap
是 Map
的变体,它除了具有 Map
的特性外,还具有顺序性,当开发者遍历 OrderedMap
的实例时,遍历顺序为该实例中元素的声明、添加顺序。OrderedMap
比非有序Map
更昂贵,并且可能消耗更多的内存。如果真要求遍历有序,请使用List
4、List
List
数据类型,对应原生 Array
数组。和原生数组,最大区别不存在’空位’。[, , , , ]
1 console .log(List([,,,,]).toJS());
六、API
我们主要介绍Map
和 List
创建 1、通过构造函数 Map()
构造函数不常用,一般都是通过Immutable.fromJS()
将一个JS
原生对象转换为一个Immutable
对象
2、Map()
1 2 3 4 5 6 7 8 9 10 11 12 console .log(Map ().toJS()); console .log(Map ({key : "value" }).toJS());
同Key覆盖问题
1 2 console .log(Map ([["key" , "value" ], ["key" , "value2" ], ["key1" , "value1" ]]).toJS());
3、List()
1 2 3 4 5 6 7 8 9 10 11 12 console .log(List().toJS()); console .log(List([1 ,2 ,3 ,4 ,{a :123 }]).toJS());
4、另一种方式
Map.of()
1 console .log(Map .of('key1' ,'value1' ,'key2' ,'value2' ,'key3' ,'value3' ).toJS());
List.of()
1 console .log(List.of({x :1 }, 2 , [3 ], 4 ).toJS());
判断是否是一个Map或者List 1、Map判断
判断是否是一个Map
, 对原生Object
不生效
1 2 console .log(Map .isMap({})); console .log(Map .isMap(Map ({})));
2、List判断
判断是否是一个List
, 对原生Array
不生效
1 2 console .log(List.isList([])); console .log(List.isList(List([])));
获取大小 1、size
1 2 3 4 5 6 7 console .log(List([1 ,2 ,3 ,4 ]).size);console .log(List.of(1 , 2 , 3 , 4 ).size);console .log(Map ({key : "value2" , key1 : "value1" }).size);console .log(Map .of({x :1 }, 2 , [3 ], 4 ).size);
count()
1 2 3 4 5 6 7 8 9 10 11 12 13 console .log(Immutable.fromJS({key : "value2" , key1 : "value1" }).count());console .log(Immutable.fromJS({key : 1 , key1 : 34 }).count((value, key, obj ) => { return value > 3 ; })); console .log(Immutable.fromJS([1 , 2 , 5 , 6 ]).count());console .log(Immutable.fromJS([1 , 2 , 5 , 6 ]).count((value, index, array ) => { return value > 3 ; }));
countBy()
countBy()
和count()
的区别就是它的返回值是一个对象。
1 2 3 4 5 6 7 8 9 console .log(Immutable.fromJS({key : 1 , key1 : 34 }).countBy((value, key, obj ) => { return value > 3 ; }).toJS()); console .log(Immutable.fromJS([1 , 2 , 5 , 6 ]).countBy((value, index, array ) => { return value > 3 ; }).toJS());
添加元素 1、Set
1 2 3 4 5 6 7 8 9 10 11 const $obj1 = Map ({a : {a1 : 34 }, b : 2 , c : 3 , d : 444 });console .log($obj1.set('a' , 0 ).toJS()); console .log($obj1.set('e' , 99 ).toJS()); const $arr1 = List([1 , 2 , 3 ]);console .log($arr1.set(-1 , 0 ).toJS()); console .log($arr1.set(4 , 0 ).toJS());
2、setIn
1 2 3 4 5 console .log(Immutable.fromJS([1 , 2 , 3 , {a : 45 , b : 64 }]).setIn(['3' , 'a' ], 1000 ).toJS());console .log(Immutable.fromJS([1 , 2 , 3 , {a : 45 , b : 64 }]).setIn(['3' , 'a' ], 1000 ).toJS());
List 特有的添加元素 1、插入元素
1 2 3 console .log(Immutable.fromJS([1 , 2 , 3 ]).insert(1 , 1.5 ).toJS());
2、设置size
1 console .log(List([]).setSize(2 ).toJS());
3、pop、push、shift、unshift
List
数据类型也拥有pop
、push
、shift
、unshift
这四种操作方法,和原生Array
的四种方法使用方式一致,但唯一区别就是返回新的List
,并且不改变原来的数组本身,而原生则是会改变元素本身
1 2 3 4 5 6 const $test = List([1 , 2 , 3 , 4 ]);console .log($test.pop().toJS(), $test.toJS()); const test = [1 , 2 , 3 , 4 ];console .log(test.pop(), test);
4、花样插入
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 console .log(Immutable.fromJS([1 , 2 , 5 , 6 ]).interpose(5555 ).toJS()); console .log(Immutable.fromJS([1 , 2 , 5 , 6 ]).interleave(Immutable.fromJS([555 , 666 ])).toJS()); console .log(Immutable.fromJS([1 , 2 , 5 , 6 ]).zip(Immutable.fromJS([555 , 666 ]).toJS())); console .log(Immutable.fromJS([1 , 2 , 5 , 6 ]).zipWith((a, b ) => { return a + b; }, Immutable.fromJS([555 , 666 ]).toJS()));
删除元素 1、delete(key)
1 2 3 4 5 6 7 8 9 console .log(Immutable.fromJS([1 , 2 , 3 ]).delete(1 ).toJS(), $arr1.toJS());console .log(Immutable.fromJS([1 , 2 , 3 ]).delete(77 ).toJS(), $arr1.toJS(), '超过范围不会强制报错' );console .log(Immutable.fromJS({a : {a1 : 34 }, b : 2 , c : 3 , d : 444 }).delete('c' ).toJS(), $obj1.toJS());console .log(Immutable.fromJS({a : {a1 : 34 }, b : 2 , c : 3 , d : 444 }).delete('asdfasfd' ).toJS(), $obj1.toJS());
2、deleteIn
和 setIn
使用方式一致
3、清空元素 lear()
1 2 3 4 5 console .log(Immutable.fromJS([1 , 2 , 3 ]).clear().toJS());console .log(Immutable.fromJS({a : {a1 : 34 }, b : 2 , c : 3 , d : 444 }).clear().toJS());
修改元素
修改某一个元素
1、set setIn
上面已经介绍过
2、update
update(key: K, notSetValue: V, updater: (value: V) => V): Map<K, V>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 const $arr1 = Immutable.fromJS([1 , 2 , 3 ]);console .log($arr1.update('2' , (value)=> { return value * 2 ; }).toJS(), $arr1.toJS()); console .log($arr1.update('6' , 1 , (value)=> { return value * 2 ; }).toJS(), $arr1.toJS()); console .log($arr1.update('6' , 0 , (value)=> { return value * 2 ; }).toJS(), $arr1.toJS()); const $obj1 = Immutable.fromJS({a : {a1 : 34 }, b : 2 , c : 3 , d : 444 });console .log($obj1.update('a' , (value)=> { return value * 2 ; }).toJS(), $obj1.toJS()); console .log($obj1.update('e' , 1 , (value)=> { return value * 2 ; }).toJS(), $obj1.toJS()); console .log($obj1.update('e' , 0 , (value)=> { return value * 2 ; }).toJS(), $obj1.toJS());
3、updateIn
使用方式和setIn一样。
获取某个元素值 1、get getIn
使用方式:get(key: number, notSetValue?: T)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 const $test = Immutable.fromJS([1111111 , 22222 , {a : 888123 }]);console .log($test.get(0 )); console .log(Immutable.fromJS({1 : 'abc' }).get(1 ), Immutable.fromJS({1 : 'abc' }).get('1' ));console .log($test.get(11 , 'no have value' )); console .log($test.getIn(['2' , 'a' ], 'child no have value' )); console .log($test.getIn(['2' , 'b' ], 'child no have value' )); const $test = Immutable.fromJS({a : {a1 : 222 }, b : 2 , c : 3 , d : 444 });console .log($test.get('a' )); console .log($test.get('v' , 'no have value' )); console .log($test.getIn(['a' , 'a1' ], 'child no have value' )); console .log($test.getIn(['d' , 'b1' ], 'child no have value' ));
2、获取头、尾元素
1 2 3 4 5 6 7 8 9 const $arr1 = Immutable.fromJS([1 , 2 , 3 ]);console .log($arr1.first());console .log($arr1.last());Immutable.fromJS({a : {a1 : 34 }, b : 2 , c : 3 , d : 444 }); console .log($obj1.first());console .log($obj1.last());
查找某个元素 1、find() findLast()
find()
、findLast()
返回 value
1 2 3 4 5 6 7 8 9 console .log(Immutable.fromJS([1 , 2 , 56 , {a : {b : 111 }}]).find((value, index, array ) => { return index === 3 ; }).toJS()); console .log(Immutable.fromJS({a : {a1 : 222 }, b : 2 , c : 3 , d : 444 }).find((value, key, obj ) => { return value === 3 ; }));
2、findKey() findLastKey()
findKey()
、findLastKey()
返回 key
1 2 3 4 5 6 7 8 9 console .log(Immutable.fromJS([1 , 2 , 3 , {a : {b : 111 }}]).findKey((value, index, array ) => { return index === 3 ; })); console .log(Immutable.fromJS({a : {a1 : 222 }, b : 2 , c : 3 , d : 444 }).findKey((value, key, obj ) => { return value === 3 ; }));
3、findEntry() findLastEntry()
findEntry()
、findLastEntry()
返回 key:value
1 2 3 4 5 6 7 8 9 console .log(Immutable.fromJS([1 , 2 , 3 , {a : {b : 111 }}]).findEntry((value, index, array ) => { return index === 3 ; })); console .log(Immutable.fromJS({a : {a1 : 222 }, b : 2 , c : 3 , d : 444 }).findEntry((value, key, obj ) => { return Immutable.is(value, Immutable.fromJS({a1 : 222 })); }));
4、keyOf() lastKeyOf()
keyOf()
、lastKeyOf()
根据 value
返回key
。
1 2 3 4 5 6 7 / List console .log(Immutable.fromJS([1 , 2 , 3 , {a : {b : 111 }}]).keyOf(Immutable.fromJS({a : {b : 111 }}))); console .log(Immutable.fromJS([1 , 2 , 3 , {a : {b : 111 }}]).keyOf(2 )); console .log(Immutable.fromJS({a : {a1 : 222 }, b : 2 , c : 3 , d : 444 }).keyOf(Immutable.fromJS({a1 : 222 }))); console .log(Immutable.fromJS({a : {a1 : 222 }, b : 2 , c : 3 , d : 444 }).keyOf(2 ));
List 特有查找某个元素 1、indexOf() lastIndexOf()
1 2 console .log(Immutable.fromJS([1 , 2 , 3 , {a : {b : 111 }}]).indexOf(Immutable.fromJS({a : {b : 111 }})));
2、findIndex() findLastIndex()
1 2 3 console .log(Immutable.fromJS([1 , 2 , 3 , {a : {b : 111 }}]).findIndex((value, index, array ) => { return value/3 === 1 ; }));
查找最大、最小元素
max()
、maxBy()
默认比较规则为>
,min()
、minBy()
默认比较规则为>
1、max()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 console .log(Immutable.fromJS([1 , 2 , 301 , 88 ]).max()); console .log(Immutable.fromJS([1 , 2 , 301 , 88 ]).max((valueA, valueB ) => { return valueA > valueB; })); console .log(Immutable.fromJS({a : 8888 , b : 2 , c : 3 , d : 444 }).max()); console .log(Immutable.fromJS({a : 8888 , b : 2 , c : 3 , d : 444 }).max((valueA, valueB ) => { return valueA > valueB; }));
2、maxBy()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 console .log(Immutable.fromJS([{a : 2 }, {a : 1 }, {a : 2301 }, {a : 222 }]).maxBy((value, index, array ) => { return value.get('a' ); }).toJS()); console .log(Immutable.fromJS([{a : 2 }, {a : 1 }, {a : 2301 }, {a : 222 }]).maxBy((value, index, array ) => { return value.get('a' ); }, (valueA, valueB) => { return valueA > valueB; }).toJS()); console .log(Immutable.fromJS({a : {a1 : 222 }, b : {a1 : 11 }, c : {a1 : 33 }, d : {a1 : 54654 }}).maxBy((value, key, obj ) => { return value.get('a1' ); }).toJS()); console .log(Immutable.fromJS({a : {a1 : 222 }, b : {a1 : 11 }, c : {a1 : 33 }, d : {a1 : 54654 }}).maxBy((value, key, obj ) => { return value.get('a1' ); }, (valueA, valueB) => { return valueA > valueB; }).toJS());
3、min()
4、minBy()
5、keys() values() entries()
获取ES6 Iterable
迭代器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 const $test = List([11 , 22 , 33 , 44 ]);const keys = $test.keys();for (let i of keys) { console .log(i); } const values = $test.values();for (let i of values) { console .log(i); } const entries = $test.entries();for (let i of entries) { console .log(i); } const $test = Immutable.fromJS({a : {a1 : 222 }, b : 2 , c : 3 , d : 444 });const keys = $test.keys();for (let i of keys) { console .log(i); } const values = $test.values();for (let i of values) { console .log(i); } const entries = $test.entries();for (let i of entries) { console .log(i); }
截取 1、slice()
和原生Array slice()
用法一致
1 2 3 4 5 6 console .log(Immutable.fromJS([1 , 2 , 3 ]).slice(0 ).toJS());console .log(Immutable.fromJS({a : {a1 : 34 }, b : 2 , c : 3 , d : 444 }).slice(0 ).toJS());console .log(Immutable.fromJS({a : {a1 : 34 }, b : 2 , c : 3 , d : 444 }).slice(1 ).toJS());
2、rest() butLast()
1 2 3 4 5 6 7 8 9 10 11 / List console .log(Immutable.fromJS([1 , {a : 1 }, 3 , 4 , 5 , 6 ]).rest().rest().toJS()); console .log(Immutable.fromJS([1 , {a : 1 }, 3 , 4 , 5 , 6 ]).butLast().toJS()); console .log(Immutable.fromJS({a : {a1 : 222 }, b : 2 , c : 3 , d : 444 }).rest().rest().toJS()); console .log(Immutable.fromJS({a : {a1 : 222 }, b : 2 , c : 3 , d : 444 }).butLast().toJS());
3、skip() skipLast() skipWhile() skipUntil()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 console .log(Immutable.fromJS([1 , {a : 1 }, 3 , 4 , 5 , 6 ]).skip(2 ).toJS()); console .log(Immutable.fromJS([1 , {a : 1 }, 3 , 4 , 5 , 6 ]).skipLast(2 ).toJS()); console .log(Immutable.fromJS([111 , 33 , 22 , 44 , 55 , 66 ]).skipWhile((value, index, array ) => { return value > 31 ; }).toJS()); console .log(Immutable.fromJS([32 , 33 , 40 , 44 , 55 , 66 ]).skipWhile((value, index, array ) => { return value < 39 ; }).toJS()); console .log(Immutable.fromJS({a : {a1 : 222 }, b : 2 , c : 3 , d : 444 }).skip(2 ).toJS()); console .log(Immutable.fromJS({a : {a1 : 222 }, b : 2 , c : 3 , d : 444 }).skipLast(2 ).toJS()); console .log(Immutable.fromJS({a : 1 , b : 2 , c : 3 , d : 444 }).skipWhile((value, key, obj ) => { return value === 1 ; }).toJS()); console .log(Immutable.fromJS({a : 5 , b : 2 , c : 3 , d : 444 }).skipWhile((value, key, obj ) => { return value < 39 ; }).toJS());
4、take() takeLast() takeWhile() takeUntil()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 console .log(Immutable.fromJS([1 , {a : 1 }, 3 , 4 , 5 , 6 ]).take(2 ).toJS()); console .log(Immutable.fromJS([1 , {a : 1 }, 3 , 4 , 5 , 6 ]).takeLast(2 ).toJS()); console .log(Immutable.fromJS([111 , 33 , 22 , 44 , 55 , 66 ]).takeWhile((value, index, array ) => { return value > 31 ; }).toJS()); console .log(Immutable.fromJS([32 , 33 , 40 , 44 , 55 , 66 ]).takeUntil((value, index, array ) => { return value > 41 ; }).toJS()); console .log(Immutable.fromJS({a : 5 , b : 2 , c : 3 , d : 444 }).take(2 ).toJS()); console .log(Immutable.fromJS({a : 5 , b : 2 , c : 3 , d : 444 }).takeLast(2 ).toJS()); console .log(Immutable.fromJS({a : 5 , b : 2 , c : 3 , d : 444 }).takeWhile((value, key, obj ) => { return value > 2 ; }).toJS()); console .log(Immutable.fromJS({a : 5 , b : 2 , c : 3 , d : 444 }).takeUntil((value, key, obj ) => { return value > 39 ; }).toJS());
循环遍历 1、map() filter() every() some() forEach() reduce() reduceRight()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 console .log(Immutable.fromJS([1 , 2 , 3 , 4 , 5 ]).map((value, index, array )=> { return value * 2 ; }).toJS()); console .log(Immutable.fromJS([1 , 2 , 3 , 4 , 5 ]).filter((value, index, array )=> { return value % 2 === 0 ; }).toJS()); console .log(Immutable.fromJS([1 , 2 , 3 , 4 , 5 ]).every((value, index, array )=> { return value > 2 ; })); console .log(Immutable.fromJS([1 , 2 , 3 , 4 , 5 ]).some((value, index, array )=> { return value > 2 ; })); console .log(Immutable.fromJS([1 , 2 , 3 , 4 , 5 , {a : 123 }]).forEach((value, index, array )=> { console .log(value, index, array.toJS(), 'forEach' ); return value < 5 ; })); console .log(Immutable.fromJS({a : 5 , b : 2 , c : 3 , d : 444 }).map((value, key, obj )=> { return value * 2 ; }).toJS()); console .log(Immutable.fromJS({a : 5 , b : 2 , c : 3 , d : 444 }).filter((value, key, obj )=> { return value % 2 === 0 ; }).toJS()); console .log(Immutable.fromJS({a : 5 , b : 2 , c : 3 , d : 444 }).every((value, key, obj )=> { return value > 2 ; })); console .log(Immutable.fromJS({a : 5 , b : 2 , c : 3 , d : 444 }).some((value, key, obj )=> { return value > 2 ; })); console .log(Immutable.fromJS({a : 5 , b : 2 , c : 3 , d : 444 }).forEach((value, key, obj )=> { return value < 444 ; }));
Map 特有 mapKeys() mapEntries()
对Map
元素进行处理,返回处理后的对象
1 2 3 4 5 6 7 8 9 10 console .log(Immutable.fromJS({a : 5 , b : 2 , c : 3 , d : 444 }).mapKeys((key )=> { return key + 'hhh' ; }).toJS()); console .log(Immutable.fromJS({a : 5 , b : 2 , c : 3 , d : 444 }).mapEntries(([key, value] )=> { return [key + 'aaa' , value+'hhhh' ]; }).toJS());
merge merge() mergeDeep() mergeWith() mergeDeepWith()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 const $test = Immutable.fromJS([1 , 2 , 3 , 7 , {a : {b : 55 , c : 66 }}]);const $test1 = Immutable.fromJS([1 , 2 , 3 , 6 , {a : {b : 333 , d : 67 }}]);console .log($test.merge($test1).toJS(), $test.toJS());console .log($test.mergeDeep($test1).toJS(), $test.toJS());console .log($test.mergeWith((prev, next )=> { return prev; }, $test1).toJS(), $test1.toJS()); console .log($test.mergeDeepWith((prev, next )=> { return prev; }, $test1).toJS(), $test1.toJS()); const $test = Immutable.fromJS({a : {a1 : 222 , a3 : 456 }, b : 2 , c : 3 , d : 444 });const $test1 = Immutable.fromJS({a : {a1 : 222 , a2 : 234 }, b : 2 , c : 3 , d : 444 });console .log($test.merge($test1).toJS(), $test.toJS());console .log($test.mergeDeep($test1).toJS(), $test.toJS());console .log($test.mergeWith((prev, next )=> { return prev; }, $test1).toJS(), $test1.toJS()); console .log($test.mergeDeepWith((prev, next )=> { return prev; }, $test1).toJS(), $test1.toJS());
jonin() 转换为字符串
使用方式和原生Array
的join()
一样
1 2 3 4 console .log(Immutable.fromJS([1 , 2 , 3 , {a : 123 , b : 321 }]).join()); console .log(Immutable.fromJS({b : 2 , a : {a1 : 222 , a3 : 456 }, c : 3 , d : 444 }).join());
isEmpty() 判空 1 2 3 4 console .log(Immutable.fromJS([]).isEmpty()); console .log(Immutable.fromJS({}).isEmpty());
has() hasIn() 检查是否有某个key 1 2 3 4 5 6 7 console .log(Immutable.fromJS([1 , 2 , 3 , {a : 123 , b : 321 }]).has('0' )); console .log(Immutable.fromJS([1 , 2 , 3 , {a : 123 , b : 321 }]).hasIn([3 , 'b' ])); console .log(Immutable.fromJS({b : 2 , a : {a1 : 222 , a3 : 456 }, c : 3 , d : 444 }).has('a' )); console .log(Immutable.fromJS({b : 2 , a : {a1 : 222 , a3 : 456 }, c : 3 , d : 444 }).hasIn(['a' , 'a3' ]));
includes() 是否包含某些元素
includes()
、contains()
这俩等效
1 2 3 4 5 6 7 8 9 10 11 console .log(Immutable.fromJS([6 , 5 , 4 , 3 , 2 , 1 , 89 ]).includes('89' ));console .log(Immutable.fromJS([6 , 5 , 4 , 3 , 2 , 1 , '89' ]).contains('89' ));console .log(Immutable.fromJS([6 , 5 , 4 , 3 , 2 , 1 , Immutable.fromJS([6 , 5 , 4 ])]).contains(Immutable.fromJS([6 , 5 , 4 ])));console .log(Immutable.fromJS({b : 2 , a : {a1 : 222 , a3 : 456 }, c : 3 , d : 89 }).includes('89' ));console .log(Immutable.fromJS({b : 2 , a : {a1 : 222 , a3 : 456 }, c : 3 , d : '89' }).contains('89' ));console .log(Immutable.fromJS({b : 2 , a : {a1 : 222 , a3 : 456 }, c : 3 , d : Immutable.fromJS([6 , 5 , 4 ])}).contains(Immutable.fromJS([6 , 5 , 4 ])));
isSubset() 子集判断 1 2 3 4 5 6 7 8 9 10 11 console .log(Immutable.fromJS([6 , 5 , 1 , [6 , 5 , 4 ]]).isSubset(Immutable.fromJS([[6 , 5 , 4 ], 6 , 5 , 4 , 3 , 2 , 1 , '89' ])));console .log(Immutable.fromJS([[6 , 5 , 4 ], 6 , 5 , 4 , 3 , 2 , 1 , '89' ]).isSuperset(Immutable.fromJS([6 , 5 , 1 , [6 , 5 , 4 ]])));console .log(Immutable.fromJS({b : 2 , a : {a1 : 222 , a3 : 456 }}).isSubset(Immutable.fromJS({b : 2 , a : {a1 : 222 , a3 : 456 }, c : 3 , d : 5 })));console .log(Immutable.fromJS({b : 2 , a : {a1 : 222 , a3 : 456 }, c : 3 , d : 5 }).isSuperset(Immutable.fromJS({b : 2 , a : {a1 : 222 , a3 : 456 }})));
reverse() 反转 1 2 3 4 5 6 console .log(Immutable.fromJS([1 , 2 , 3 , 4 , 5 , 6 ]).reverse().toJS());console .log(Immutable.fromJS({b : 2 , a : {a1 : 222 , a3 : 456 }, c : 3 , d : 5 }).reverse().toJS());
排序
sort()
和sortBy()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 console .log(Immutable.fromJS([6 , 5 , 4 , 3 , 2 , 1 ]).sort().toJS());console .log(Immutable.fromJS([1 , 2 , 3 , 4 , 5 , 6 ]).sort((a, b ) => { if (a < b) { return -1 ; } if (a > b) { return 1 ; } if (a === b) { return 0 ; } }).toJS()); console .log(Immutable.fromJS([{a : 1 , b : {c : 22 }}, {a : 2 , b : {c : 22 }}, {a : 1 , b : {c : 22 }}, {a : 3 , b : {c : 22 }}, {a : 10 , b : {c : 22 }}, {a : 9 , b : {c : 22 }}]).sortBy((value, index, array )=> { return value.get('a' ) },(a, b) => { if (a < b) { return -1 ; } if (a > b) { return 1 ; } if (a === b) { return 0 ; } }).toJS()); console .log(Immutable.fromJS({b : 2 , a : 88 , c : 3 , d : 5 }).sort().toJS());console .log(Immutable.fromJS({b : 2 , a : 88 , c : 3 , d : 5 }).sort((a, b ) => { if (a < b) { return -1 ; } if (a > b) { return 1 ; } if (a === b) { return 0 ; } }).toJS()); console .log(Immutable.fromJS({b : {a : 2 }, a : {a : 88 }, c : {a : 3 }, d : {a : 5 }}).sortBy((value, key, obj )=> { return value.get('a' ) },(a, b) => { if (a < b) { return -1 ; } if (a > b) { return 1 ; } if (a === b) { return 0 ; } }).toJS());
flatten() 平铺
参数默认情况下,false
深度平铺,true
浅度平铺1层
1 2 3 4 5 6 7 8 9 10 11 console .log(Immutable.fromJS([1 , 2 , 3 , 4 , [1 , 11 , 111 , 12344 ], {a : 1234 , b : {bb : [777 , 888 ]}}, 5 , 6 ]).flatten().toJS());console .log(Immutable.fromJS([1 , 2 , 3 , 4 , [1 , 11 , 111 , 12344 ], {a : 1234 , b : {bb : [777 , 888 ]}}, 5 , 6 ]).flatten(true ).toJS());console .log(Immutable.fromJS({b : 2 , a : {a1 : {a5 : 333 }, a3 : [1 ,2 ,3 ]}, c : 3 , d : 5 }).flatten().toJS());console .log(Immutable.fromJS({b : 2 , a : {a1 : {a5 : 333 }, a3 : [1 ,2 ,3 ]}, c : 3 , d : 5 }).flatten(true ).toJS());
groupBy() 分组
返回值是OrderedMap
1 2 3 4 5 6 7 8 9 10 11 console .log(Immutable.fromJS([{v : 0 , a : 111 }, {v : 1 , a : {b : [1 , 2 , 3 ]}}, {v : 1 , a : 333 }, {v : 0 , a : {b : [1 , 2 , 3 ]}}, {v : 1 , a : 333 }]).groupBy((value ) => { return value.get('a' ) }).toJS()); console .log(Immutable.fromJS({b : {a5 : 333 }, a : {a5 : 333 }, c : {a5 : 334 }, d : {a5 : 334 }}).groupBy((value ) => { return value.get('a5' ) }).toJS());
flip() Map 特有翻转 1 console .log(Immutable.fromJS({b : 'b1' , a : 'a1' , c : 'c1' , d : 'd1' }).flip().toJS());
连接 concat() 1 2 3 4 5 6 7 8 9 10 11 const $test1 = Immutable.fromJS([1 , 2 , 3 , 4 , 5 , 6 ]);const $test2 = Immutable.fromJS([111 , 222 , 333 , 444 , 555 , 666 ]);console .log($test1.concat($test2).toJS()); console .log($test1.toJS(), $test2.toJS()); const $test1 = Immutable.fromJS({b : 2 , a : {a1 : {a5 : 333 }, a3 : [1 ,2 ,3 ]}, c : 3 , d : 5 });const $test2 = Immutable.fromJS({b1 : 22 , b : 34 });console .log($test1.concat($test2).toJS()); console .log($test1.toJS(), $test2.toJS());
类型转换 1、转换为原生类型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 console .log(Immutable.fromJS([1 , 2 , 3 , 4 , 5 , 6 , {a : {b : [1234 , 22 ]}}]).toArray());console .log(Immutable.fromJS([1 , 2 , 3 , 4 , 5 , 6 , [1234 , 22 ]]).toArray());console .log(Immutable.fromJS([1 , 2 , 3 , 4 , 5 , 6 , {a : {b : [1234 , 22 ]}}]).toObject());console .log(Immutable.fromJS([1 , 2 , 3 , 4 , 5 , 6 , [1234 , 22 ]]).toObject());console .log(Immutable.fromJS({b : 2 , a : {a1 : {a5 : 333 }, a3 : [1 ,2 ,3 ]}, c : 3 , d : 5 }).toArray());console .log(Immutable.fromJS({b : 2 , a : [1 , 2 , 2 ], c : 3 , d : 5 }).toArray());console .log(Immutable.fromJS({b : 2 , a : {a1 : {a5 : 333 }, a3 : [1 ,2 ,3 ]}, c : 3 , d : 5 }).toObject());console .log(Immutable.fromJS({b : 2 , a : [1 , 2 , 2 ]}).toObject());
2、转换为其他ImmutableJS数据类型
七、和React Redux 架构的结合
利用 immutable.js
不可变的特性,可以极大的优化React render
的冗余执行。React
官方提供的PureRenderMixin
是浅比较
1、immutable-pure-render-decorator
专门针对immutable
的PureRenderMixin
,用来装饰React
组件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 import {React} from 'base' ;import pureRenderDecorator from '../../../widgets/libs/immutable-pure-render-decorator' ;@pureRenderDecorator export default class PartA extends React .Component { constructor (props) { super (props); } render() { console .log('组件PartA,render执行了' ); const data = this .props.data; return ( <section> <div> <p>我是组件PartA</p> <p>{data.toJSON ? JSON.stringify(data.toJSON()) : data}</ p> </div> </ section> ) } }
2、优化shouldComponentUpdate()
我们都知道官方提供的React.addons.PureRenderMixin
提供的shouldComponentUpdate()
,只能进行浅比较,对于引用类型Object
、Array
比较无力,而如果使用Immutable
的Map
和List
替换Object
、Array
,则可以使用Immutable.is()
来比较两个引用类型,从而补充了React.addons.PureRenderMixin
的漏洞。
3、高阶组件封装
对于使用immutable.js
的项目,在应用公共组件的时候,由于公共组件的内部实现一定是原生
JS数据,所以我们只能传递原生
JS数据到公共组件,但是如果转换成了原生
JS数据,就又会出现"
React.addons.PureRenderMixin提供的
shouldComponentUpdate()`是浅比较”问题,对此可以使用下面的高阶组件进行封装
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 import {React} from 'base' ;import {shouldComponentUpdate} from '../immutable-pure-render-decorator' ;export default ComposedComponent => { return class extends React .Component { constructor (props) { super (props); this .shouldComponentUpdate = shouldComponentUpdate.bind(this ); } render() { const props = this .props.toJS ? this .props.toJS() : this .props; return <ComposedComponent {...this.props } {...props } /> ; } } };
3、Demo
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 import {React} from 'base' ;import { connect } from 'react-redux' ;import highComponent from '../../../../widgets/libs/utils/highComponent' ;import actions from '../../actions' ;import Dialog from '@alife/dialog' ;function mapStateToProps (state ) { return { open: state.getIn(['dialog' , 'open' ]), title: state.getIn(['dialog' , 'title' ]) } } function mapDispatchToProps (dispatch ) { return { onPrimaryTouchTap: () => { dispatch(actions.toggleDialog(false )); }, onSecondaryTouchTap: () => { dispatch(actions.toggleDialog(false )); } } } export default connect(mapStateToProps, mapDispatchToProps)(highComponent(Dialog))
八、思维导图总结API
九、更多参考
facebook.github.io/immutable-js/docs/