Immutable.js

immutable出现的背景

在我们的日常开发中,我相信大家都遇到过一种困扰,那就是如何将后端返回的数据,深拷贝一份,再供我们自己使用呢?

如果用过React框架开发过项目的小伙伴,也一定记得Redux中的reducer是基于纯函数设计的,要求返回的状态数据(对象或数组),需要先深拷贝一份(目的是:防止影响老状态),再根据自己的开发需求对其拷贝后的值操作?

这是使用redux、reducer中修改数据的代码:

let list = [{ name: 'house', age: 18}
]let newList = [...list];
newList[0].name = "dalukuankuan";console.log(list[0].name); // "dalukuankuan"

显然上面例子中的原数组list,被我们不轻易间串改了,其实原因很简单,就是因为ES6中的展开运算符[...list]是一个浅拷贝,浅拷贝的意思就是只复制对象或数组的第一级内容。

在上面中,可以发现经过展开运算符的浅拷贝,只复制了其内层引用类型的地址,当通过索引找到其引用地址,并改变它的时候,改的就是list原数组本身。

当然,有的小伙伴可以想到:当访问到对象的属性值的时候,将属性值再进行递归对比,这样就达到了深层对比的效果,但是想想一种极端的情况,就是在属性有一万条的时候,只有最后一个属性发生了变化,那我们就不得已将一万条属性都遍历。这是非常浪费性能的。

回到问题的本质,无论是直接用浅层比对,还是进行深层比对,我们最终想知道的就是原对象里的属性有无改变。

在这样的条件下,immutable 数据应运而生。

什么是immutable数据?

「immutable」数据一种利用结构共享形成的持久化数据结构,一旦有部分被修改,那么将会返回一个全新的对象,并且原来相同的节点会直接共享。

每次修改一个 immutable 对象时都会创建一个新的不可变的对象,在新对象上操作并 不会影响到原对象的数据。

具体点来说,「immutable」 对象数据内部采用是多叉树的结构,凡是有节点被改变,那么它和与它相关的所有上级节点更新

用一张动图来模拟一下这个过程:

是吧!只更新了父节点,比直接比对所有的属性简直强太多,并且更新后返回了一个全新的引用,即使是浅比对也能感知到数据的改变。

因此,采用 「immutable」 既能够最大效率地更新数据结构,又能够和现有的 React中的 「PureComponent (memo)」 顺利对接,感知到状态的变化,是提高 React 渲染性能的极佳方案

不过,immutable 也有一些被开发者吐槽的点,首先是 immutable 对象和 JS 对象要注意转换,不能混用,这个大家注意适当的时候调用 toJS 或者 fromJS 即可,问题并不大。

immutable优化性能的方式

immutable实现的原理是:持久化数据结构,也就是使用旧数据创建新数据时,要保证旧数据同时可用且不变。同时为了避免deepCopy 把所有节点都复制一遍带来的性能损耗

immutable使用了结构共享,即如果对象树中一个节点发生变化,只修改这个节点和受它影响的父节点,其它节点则进行共享。

与React中的 「PureComponent(memo)」 相结合,我们知道PureComponent能够在内部帮我们比较新props旧props新state旧state,如果值相等或者对象含有的相同的属性、且属性值相等,便确定shouldComponentUpdate返回true或者false,从而判断是否再次渲染render函数。

看上述代码,我们可以看出来,当代码中使用immutable第三库的时候,可以精确地深拷贝 a 对象,改a对象中的select属性赋值给b之后,并不会影响原对象a,而bselect属性变味了新值。

如果上述select属性给一个组件用,因为其值被改变了,导致shouldComponentUpdate应该返回true,而filter属性给另一个组件用,通过判断,并无变化,导致shouldComponentUpdate应该返回false,故此组件就避免了重复的diff算法对比,大大提高了React中的性能优化。

这么好用的第三方库,我们来看一下它的基本用法:

immutable中常用类型

(1)Map() 包裹对象

const { Map } = require('immutable');
const map1 = Map({ a: 1, b: 2, c: 3 });
const map2 = map1.set('b', 50);console.log(map1.get('b')); // 2
console.log(map2.get('b')); // 50

(2)List() 包裹数组

const { List } = require('immutable');const list1 = List([ 1, 2 ]);
const list2 = list1.push(3, 4, 5);  // [1,2,3,4,5]
const list3 = list2.unshift(0);    // [0,1,2,3,4,5]
const list4 = list1.concat(list2, list3); // [1,2,3,4,5,0,1,2,3,4,5]//push, set, unshift or splice 都可以直接用,返回一个新的immutable对象

(3)merge() 连接对象 | concat() 连接数组

const { Map, List } = require('immutable');const map1 = Map({ a: 1, b: 2, c: 3, d: 4 });
const map2 = Map({ c: 10, a: 20, t: 30 });
const obj = { d: 100, o: 200, g: 300 };const map3 = map1.merge(map2, obj);
// Map { a: 20, b: 2, c: 10, d: 100, t: 30, o: 200, g: 300 }const list1 = List([ 1, 2, 3 ]);
const list2 = List([ 4, 5, 6 ]);const list3 = list1.concat(list2, array);
// List [ 1, 2, 3, 4, 5, 6, 7, 8, 9 ]

(4)toJS() 把immutable对象转换为js对象

const { Map, List } = require('immutable');const deep = Map({ a: 1, b: 2, c: List([ 3, 4, 5 ]) });
console.log(deep.toObject());   // { a: 1, b: 2, c: List [ 3, 4, 5 ] }
console.log(deep.toArray());    // [ 1, 2, List [ 3, 4, 5 ] ]
console.log(deep.toJS());       // { a: 1, b: 2, c: [ 3, 4, 5 ] }
JSON.stringify(deep);           // '{"a":1,"b":2,"c":[3,4,5]}'

(5)fromJS() 包裹 js对象转换为immutable对象

const { fromJS } = require('immutable');const nested = fromJS({ a: { b: { c: [ 3, 4, 5 ] } } });
// Map { a: Map { b: Map { c: List [ 3, 4, 5 ] } } }const nested2 = nested.mergeDeep({ a: { b: { d: 6 } } });
// Map { a: Map { b: Map { c: List [ 3, 4, 5 ], d: 6 } } }console.log(nested2.getIn([ 'a', 'b', 'd' ])); // 6
//如果取一级属性 直接通过get方法,如果取多级属性 getIn(["a","b","c"]])// setIn 设置新的值
const nested3 = nested2.setIn([ 'a', 'b', 'd' ], "kerwin");
// Map { a: Map { b: Map { c: List [ 3, 4, 5 ], d: "kerwin" } } }// updateIn 回调函数更新
const nested3 = nested2.updateIn([ 'a', 'b', 'd' ], value => value + 1);
console.log(nested3);
// Map { a: Map { b: Map { c: List [ 3, 4, 5 ], d: 7 } } }const nested4 = nested3.updateIn([ 'a', 'b', 'c' ], list => list.push(6));
// Map { a: Map { b: Map { c: List [ 3, 4, 5, 6 ], d: 7 } } }

相对较全的immutable一些常用方法,都在这里给大家总结了,大家在项目中经常用就可以熟练掌握了。

immutable+Redux的开发方式

情况1:未使用immutable

下面的代码的情况十分危险,不建议这样用,因为一旦当newStateList中的类型较为复杂(包含引用类型),且需要修改newStateList时,就会发生报错,因为[...xxx, ...xxx]是浅拷贝,会影响原来的状态。

情况2:使用immutable

通过store中传递过来的老状态prevState先转化为immutable对象,对深拷贝之后的对象,再进行修改等操作时,不会影响原状态,最后再通过toJS()转换为js对象即可

Immutable的优势

1. 保证不可变(每次通过Immutable.js操作的对象都会返回一个新的对象)
2. 丰富的API
3. 性能好 (通过字典树对数据结构的共享)

Immutable的问题

1. 与原生JS交互不友好 (通过Immutable生成的对象在操作上与原生JS不同,如访问属性,myObj.prop1.prop2.prop3 => myImmutableMap.getIn([‘prop1’, ‘prop2’, ‘prop3’])。另外其他的第三方库可能需要的是一个普通的对象)
2. Immutable的依赖性极强 (一旦在代码中引入使用,很容易传播整个代码库,并且很难在将来的版本中移除)
3. 不能使用解构和对象运算符 (相对来说,代码的可读性差)
4. 不适合经常修改的简单对象 (Immutable的性能比原生慢,如果对象简单,并且经常修改,不适合用)
5. 难以调试 (可以采用 Immutable.js Object Formatter扩展程序协助)
6. 破坏JS原生对象的引用,造成性能低下 (toJs每次都会返回一个新对象)

感谢 暖眸的个人博客 - 编程圈 (bianchengquan.com)

react redux-immutable相关推荐

  1. 用 React+Redux+Immutable 做俄罗斯方块

    俄罗斯方块是一直各类程序语言热衷实现的经典游戏,JavsScript的实现版本也有很多,用React 做好俄罗斯方块则成了我一个目标. 戳 https://chvin.github.io/react- ...

  2. 基于react + redux + ES6 + webpack + react-router的英雄联盟战绩查询应用

    技术栈: react + redux + immutable + less + scss + ES6/7 + webpack + fetch + react-router按需加载 + react-tr ...

  3. react实战项目_React实战之React+Redux实现一个天气预报小项目

    引言 经过一段时间的React学习,React和Vue的开发确实有很大的不同,但是都是MVVM框架,因此上手没有很大的难度,这次用React+Redux开发一个天气预报小项目.源码地址:https:/ ...

  4. 一个 react+redux 工程实例

    在前几天的一篇文章中总结部分提到了学习过程中基础的重要性.当然,并不是不支持大家学习新的框架,这篇文章就分享一下react+redux工程实例. 一直在学习研究react.js,前前后后做了几次分享. ...

  5. 实例讲解基于 React+Redux 的前端开发流程

    前言:在当下的前端界,react 和 redux 发展得如火如荼,react 在 github 的 star 数达 42000 +,超过了 jquery 的 39000+,也即将超过前几年比较火的an ...

  6. React Redux入门

    目录 入门 我们应该什么时候使用? Redux库和工具 Redux Toolkit Redux DevTools 扩展 demo练习准备工作: 基础示例 Redux Toolkit示例 Redux术语 ...

  7. React+Redux开发实录(一)搭建工程脚手架

    React+Redux开发实录(一)搭建工程脚手架 React+Redux开发实录(二)React技术栈一览 搭建工程脚手架 准备工作 安装node 安装git 安装一款前端IDE 推荐VSCode, ...

  8. 基于 react, redux 最佳实践构建的 2048

    前段时间 React license 的问题闹的沸沸扬扬,搞得 React 社区人心惶惶,好在最终 React 团队听取了社区意见把 license 换成了 MIT.不管 React license ...

  9. React Redux 的一些基本知识点

    一.React.createClass 跟 React.Component 的区别在于后者使用了ES6的语法,用constructor构造器来构造默认的属性和状态. 1. React.createCl ...

  10. 【视频】React redux toolkit创建状态切片

    React redux toolkit创建状态切片

最新文章

  1. redmine升级了 ,6与18日同时发布2.0.3和1.4.4
  2. ASP.NET中String.IndexOf 方法的使用
  3. 超时,重试,熔断,限流
  4. Boost智能指针——weak_ptr
  5. 谷歌发布最新版安卓Android,谷歌正式除名华为,安卓12华为首发无望,但鸿蒙将迎难顶上!...
  6. python中面向对象空间时间_python基础学习Day15 面向对象、类名称空间、对象名称空间 (2)...
  7. django cookie、session
  8. Qt图形界面编程入门(信号和槽通信机制)
  9. mysql的条件语句_mysql条件语句
  10. bankeralgorithm.jar中没有主清单属性_怀旧服:迅击指环和其拉之怒属性一致,为何狂暴战用迅击更好...
  11. 数据中台对企业意义和作用有哪些
  12. 老兵不死:Radionomy正式宣布收购Winamp
  13. 三屏设置相关技术收集
  14. 基于qgis和arcgis进行CAD转GIS操作
  15. 全脑地图:单个记忆被拆分存储在多个相连的大脑区域
  16. 2008 r2 server sql 中文版补丁_Microsoft SQL Server 2008 r2 sp2补丁 64位 官方免费版
  17. Halcon常见错误
  18. 安卓系统网络服务器地址,安卓系统 云服务器地址
  19. Hadoop 2.X的安装与配置
  20. 【数据挖掘】数据挖掘概述

热门文章

  1. MYSQL数据库基础篇
  2. 微信删除好友对方知道吗?
  3. sql 字段添加 和增加字段备注 删除备注
  4. 渗透测试|CSRF拿下盗图狗后台
  5. mysql 存坐标_MySql使用Float数据类型存储地理坐标
  6. 被“误解”的游戏开发者
  7. torch.transpose()
  8. Linux cp命令详解
  9. uni-app微信小程序支付
  10. 智能路由隐身 靠它进军智能家居还可行?