React性能优化的中流砥柱——Immutable数据流

编者荐语:

本文帮助你们从0了解什么是immutable,以及它有什么做用,是在什么背景下应运而生的。前端

最重要的是它是咱们平常开发中经常使用的技术之一,与React配合使用的immutable能给咱们开发者带来多大的便利,本文会依次讲到。算法

immutable出现的背景

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

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

来看下面这样一个例子:性能优化

let list = [
  { name'house'age18}
]

let newList = [...list];
newList[0].name = "xiaoming";

console.log(list[0].name); // "xiaoming"
复制代码

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

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

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

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

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

什么是immutable数据?

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

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

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

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

immutable修改节点更新引用过程
immutable修改节点更新引用过程

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

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

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

immutable优化性能的方式

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

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

immutable性能优化体如今哪里

immutable优化性能
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({ a1b2c3 }); 
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([ 12 ]); 
const list2 = list1.push(345);  // [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({ a1b2c3d4 });
const map2 = Map({ c10a20t30 });
const obj = { d100o200g300 };

const map3 = map1.merge(map2, obj);
// Map { a: 20, b: 2, c: 10, d: 100, t: 30, o: 200, g: 300 }

const list1 = List([ 123 ]);
const list2 = List([ 456 ]);

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({ a1b2c: List([ 345 ]) });
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: [ 345 ] } } });
// Map { a: Map { b: Map { c: List [ 3, 4, 5 ] } } }

const nested2 = nested.mergeDeep({ a: { b: { d6 } } });
// 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的通用性

一句话总结:immutable适用于任何框架开发,只要是能引入第三方库的框架,均可以使用。

下面简单列举一下,我在React开发中reducer的简单使用状况:

immutable+Redux的开发方式

状况1:未使用immutable

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

状况2:使用immutable

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

本文总结

immutable并无深层比较,由于深层比较的开销是很大的。

immutable数据调用set方法修改的时候仅仅修改本节点和它上面全部的相关上层节点,保证了不同的引用,也更新了数据。更重要的是,它避免了无关数据的比对,提升了性能。

看完三件事❤

若是你以为这篇内容对你还蛮有帮助,我想邀请你帮我三个小忙:

  1. 点赞,转发,有大家的『在看』,才是我创造的动力。
  2. 关注公众号 『前端时光屋』,不按期分享原创知识。
  3. 同时能够期待后续文章ing🚀
相关文章
相关标签/搜索