博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
在react/redux中使用Immutable
阅读量:4992 次
发布时间:2019-06-12

本文共 5499 字,大约阅读时间需要 18 分钟。

有人说 Immutable 可以给 React 应用带来数十倍的提升,也有人说 Immutable 的引入是近期 JavaScript 中伟大的发明,因为同期 React 太火,它的光芒被掩盖了。这些至少说明 Immutable 是很有价值的,下面我们来一探究竟。

1、什么是Immutable?

Immutable是一旦创建,就不能被更改的数据。对Immutable对象的任何修改或添加删除操作都会返回一个新的Immutable对象。Immutable实现的原理是Persistent Data Structure(持久化数据结构),也就是是永久数据创建新数据时,要保证旧数据同时可用且不变。同时为了避免deepCopy把所有节点都复制一遍带来的性能损耗,Immutable使用了Structural Sharing(结构共享),即如果对象树结点发生变化,只修改这个结点和受它影响的父节点,其他结点进行共享。

 

 

初识:

让我们看下面一段代码:

function keyLog(touchFn) {  let data = { key: 'value' };  fun(data);  console.log(data.key); // 猜猜会打印什么?}

不查看fun方法,不知道它对data做了什么,无法确认会打印什么。但如果data是Immutable,你可以确定打印的就是value:

function keyLog(touchFn) {  let data = Immutable.Map({ key: 'value' });  fun(data);  console.log(data.get('key'));  // value}

JavaScript中的Object与Array等使用的是引用赋值,如果新的对象简单的引用了原始对象,改变新的对象也将影响旧的。

foo = {a:1};  bar = foo;  bar.a = 2;foo.a // 2

虽然这样可以节约内存,但当应用复杂后,造成了状态不可控,是很大的隐患,节约内存的优点变得得不偿失。

Immutable则不一样,相应的:

foo = Immutable.Map({ a: 1});bar = foo.set('a', 2);foo.get('a') // 1

 

 简洁:

在redux中,它的最优做法是每个reducer都返回一个新的对象(数组),所以我们常常会看到这样的代码:

// reducer...return [   ...oldArr.slice(0,3),   newValue,   ...oldArr.slice(4)   ];

为了返回新的对象(数组),不得不有上面奇怪的样子,而在使用更深的数据结构时会变的更棘手。

让我们看看Immutable的做法:

// reducer...return oldArr.set(4, newValue);

是不是很简洁?

 

关于"===":

众所周知,对于Object与Array的===比较,是对引用地址的比较,而不是“值比较”,如:

{a:1, b:2, c:3} === {a:1, b:2, c:3}; // false[1, 2, [3, 4]] === [1, 2, [3, 4]]; // false

对于上面只能采用deepCopydeepCompare来比较,不仅麻烦而且耗性能。

我们感受一下Immutbale的做法:

map1 = Immutable.Map({a:1, b:2, c:3});map2 = Immutable.Map({a:1, b:2, c:3});Immutable.is(map1, map2); // true 比较值 map1 === map2;             // false 比较地址
// List1 = Immutable.List([1, 2, Immutable.List[3, 4]]);List1 = Immutable.fromJS([1, 2, [3, 4]]);List2 = Immutable.fromJS([1, 2, [3, 4]]);Immutable.is(List1, List2); // true

似乎有阵清风吹过。

Immutable.is 比较的是两个对象的 hashCode 或 valueOf(对于 JavaScript 对象)。由于 immutable 内部使用了 Trie 数据结构来存储,只要两个对象的 hashCode 相等,值就是一样的。这样的算法避免了深度遍历比较,性能非常好。

Immutable使用了Structure Sharing会尽量复用内存,甚至以前使用的对象也可以再次被复用,引用的对象会被垃圾回收。

import { Map} from 'immutable';let a = Map({  select: 'users',  filter: Map({ name: 'Cam' })})let b = a.set('select', 'people');a === b; // falsea.get('filter') === b.get('filter'); // true

上面 a 和 b 共享了没有变化的 filter 节点。

 

并发安全:

传统的并发非常难做,因为要处理各种数据不一致问题,因此『聪明人』发明了各种锁来解决。但使用了 Immutable 之后,数据天生是不可变的,并发锁就不需要了

然而现在并没什么卵用,因为 JavaScript 还是单线程运行的啊。但未来可能会加入,提前解决未来的问题不也挺好吗?

函数式编程:

Immutable本身就是函数式编程中的概念,纯函数式编程比面向对象更适用于前端开发。因为只要输入一致,输出必然一致,这样开发的组件更易于调试和组装。

像 ClojureScript,Elm 等函数式编程语言中的数据类型天生都是 Immutable 的,这也是为什么 ClojureScript 基于 React 的框架 --- Om 性能比 React 还要好的原因。

 

2、在react中使用Immutable

熟悉React的都知道,React做性能优化时有个大招,就是使用shouldComponentUpdate(),但它默认返回true,即始终会执行render()方法,后面做Virtual DOM比较,并得出是都需要做真是DOM更新,这里往往会带来很多务必要的渲染成为性能瓶颈。

在使用原生属性时,为了得出shouldComponetUpdate正确的true or false,不得不用deepCopydeepCompare来算出答案,但 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;  }  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;}

使用 Immutable 后,如下图,当红色节点的 state 变化后,不会再渲染树中的所有节点,而是只渲染图中绿色的部分:

setState 的一个技巧

React 建议把 this.state 当作 Immutable 的,因为修改前需要做一个 deepCopy,显得麻烦:

 

import '_' from 'lodash';const Component = React.createClass({  getInitialState() {    return {      data: { times: 0 }    }  },  handleAdd() {    let data = _.cloneDeep(this.state.data);    data.times = data.times + 1;    this.setState({ data: data });    // 如果上面不做 cloneDeep,下面打印的结果会是已经加 1 后的值。    console.log(this.state.data.times);  }}

使用 Immutable 后:

getInitialState() {    return {      data: Map({ times: 0 })    }  },  handleAdd() {    this.setState({ data: this.state.data.update('times', v => v + 1) });    // 这时的 times 并不会改变    console.log(this.state.data.get('times'));  }

上面的 handleAdd 可以简写成:

handleAdd() {    this.setState(({data}) => ({      data: data.update('times', v => v + 1) })    });  }

 

3、如何在Redux中使用Immutable

目标:将state -> Immutable化。

关键的库:
将原来 Redux提供的combineReducers改由上面的库提供: 

 

// rootReduers.js// import { combineReducers } from 'redux'; // 旧的方法import { combineReducers } from 'redux-immutable'; // 新的方法import prop1 from './prop1';import prop2 from './prop2';import prop3 from './prop3';const rootReducer = combineReducers({  prop1, prop2, prop3,});// store.js// 创建store的方法和常规一样import { createStore } from 'redux';import rootReducer from './reducers';const store = createStore(rootReducer);export default store;

通过新的combineReducers将把store对象转化成Immutable,在container中使用时也会略有不同(但这正是我们想要的):

const mapStateToProps = (state) => ({  prop1: state.get('prop1'),  prop2: state.get('prop2'),  prop3: state.get('prop3'),  next: state.get('next'),});export default connect(mapStateToProps)(App);

4、总结

Immutable 可以给应用带来极大的性能提升,但是否使用还要看项目情况。由于侵入性较强,新项目引入比较容易,老项目迁移需要评估迁移。对于一些提供给外部使用的公共组件,最好不要把 Immutable 对象直接暴露在对外接口中。

如果 JS 原生 Immutable 类型会不会太美,被称为 React API 终结者的 Sebastian Markbåge 有一个这样的,能否通过现在还不确定。不过可以肯定的是 Immutable 会被越来越多的项目使用。

 

转载于:https://www.cnblogs.com/greatluoluo/p/8469224.html

你可能感兴趣的文章
大图轮播
查看>>
UNIX环境高级编程读书笔记
查看>>
java awt 乱码问题
查看>>
矩阵中的路径
查看>>
unity回调函数范例
查看>>
linux下给php安装curl、gd(ubuntu)
查看>>
Java自带的Logger使用-代码摘要
查看>>
Java设计模式系列 — 构造器模式
查看>>
MySQL执行计划explain的key_len解析
查看>>
Windows Phone开发(9):关于页面状态 转:http://blog.csdn.net/tcjiaan/article/details/7292160...
查看>>
android 通过数组,流播放声音的方法
查看>>
Spring入门篇
查看>>
JAVA遇见HTML——JSP篇(JSP状态管理)
查看>>
启动eclipse出现错误Java was started but returned exit =一个数字
查看>>
myBatis模糊查找
查看>>
数据结构与算法之五 链接列表
查看>>
java 对象数组
查看>>
设计模式读书笔记-单件模式(创建型模式)
查看>>
Oracle——热备份
查看>>
Vue路由history模式踩坑记录:nginx配置解决404问题
查看>>