美文网首页让前端飞React.js前端开发
Immutable简介及在React应用中的价值

Immutable简介及在React应用中的价值

作者: A长安 | 来源:发表于2019-04-20 17:57 被阅读6次

什么是Immutable?

官网上是这么说的:

Immutable data cannot be changed once created, leading to much simpler application development, no defensive copying, and enabling advanced memoization and change detection techniques with simple logic. Persistent data presents a mutative API which does not update the data in-place, but instead always yields new updated data.

简单的说就是immutable是一种一旦创建就不能更改的数据类型,这样你在程序中就不用考虑每次对象的深浅拷贝,immutable有高级的数据更改检测机制。我们可以通过它提供的API进行数据的更改,且每次都是返回一个新的对象。

immutable.js每次进行数据操作都会返回一个新的对象,为了避免深度拷贝带来的性能损耗,immutable使用结构共享,就是说当其中一个节点发生改变之后,更改的只有这个节点本身和它的父节点们,其他节点数据则进行共享。

具体的操作API可以自行查看immutable.js官网https://immutable-js.github.io/immutable-js/

那immutable对于react应用的意义有什么呢??

React组件的重新渲染机制

1.当调用setState进行state的更改的时候
2.当父组件传递给子组件的props值改变的之后(更准确的说应该是父组件的重新渲染会带动子组件重新渲染,不管你props有没有变)

这里有个问题,其实第一条也不是完全准确的,因为当你调用setState的时候即便你state值没有改变也一样会触发re-render,请看代码:

import React, { Component } from 'react';
class App extends Component {
  constructor(props) {
    super(props);
    this.state = {
      a: 1
    }
  }
  render() {
    console.log('我render了');
    return (
      <div className="App">
        <button onClick={() => {
          const oldState = this.state;
          this.setState({
            a: oldState.a
          });
        }}>click</button>
        <span>{this.state.a}</span>
      </div>
    );
  }
}
export default App;

我每一次的点击即便没有更改state,但是还是会触发render中的console方法!!(这个地方还有待研究),初步理解为调用setState无论更改state与否,都会触发re-render.

显然上面的渲染操作是我们不希望看到的,这是因为在react组件中shouldComponentUpdate总是返回true的.
要解决上述问题就需要在shouldComponentUpdate中进行两次state的比较,简单的数据结构比较是很容易的,如果数据结构层级很深,那样的话比较起来就会很麻烦。

Immutable解决此问题:

因为immutable是结构不可变的数据类型,每次操作更改之后都会返回一个新的对象,正要是可以大大减少我们diff的过程,只需要比较两个对象在内存中的引用是否相等就可以啦。

Immutable优点:

1.安全的操作state:

react不允许我们直接修改state,但是我们是可以通过setState来修改state。但是setState执行newState和oldState的merge过程是属于浅merge,并不会去深层的递归merge,那浅merge是什么意思呢?看下面代码:

class App extends Component {
  constructor(props) {
    super(props);
    this.state = {
      a: {
        b: 2,
        c: 3
      }
    }
  }
  render() {
    console.log(this.state.a);
    return (
      <div className="App">
        <button onClick={() => {
          this.setState({
            a: {
              b: 2
            }
          });
        }}>click</button>
      </div>
    );
  }
}

可以看到这就是浅merge结果,第二次的设置state会把第一次的state覆盖掉,他定不会通过比较来决定那个字段需要覆盖掉。在实际的应用中,如果state不能deep merge的话,那么受到很大的限制,如果进行深度合并的话,一方面会带来效率上的问题,而且实现起来也比较复杂。同时这种浅merge,还有可能因为一个疏忽导致有用的字段被覆盖掉,造成程序的安全问题。

是引用immutable可以很有效的避免这个问题,因为每次操作数据返回的都已一个新对象,只需要用过immutable的set方法修改需要被改变的值,来返回一个新的state对象替换掉老的state对象,使我们操作state更加的安全。

2.节省内存

immutable.js使用了Structure Sharing,这可尽可能的复用内存,而且实现时间旅行功能也是很简单的。

let a = Immutable.Map({
  select: 'user_a',
  filter: Immutable.Map({ name: 'Cam' })
})
let b = a.set('select', 'user_b');

a === b; // false
a.get('filter') === b.get('filter'); // true

解释一下上代码的过程,首先创建了一个Map对象赋值给变量啊a, 然后a通过set方法修改select属性,新生成的Immutable对象赋值给b,虽然a,b是两个不同的对象,但是a和b共享了没有改变的filter节点,所以a.get('filter') 等于 b.get('filter')。

**Immutable为了节省性能和内存还会避免创建新的对象,如果数据没有改变的话它是不会创建新对象的: **

const originalMap = Immutable.Map({  b: 2 })
const updatedMap = Immutable.originalMap.set('b', 2)
updatedMap === originalMap // return ture

就是因为操作都会产生一个全新的Immutable对象,所以我认为可以将每次操作的产物存储在一个数组中,这个样就可以轻松拿到之前版本的数据,来完成时间旅行,版本回退的功能了。

3.拥抱函数式编程

Immutable本身就是函数式编程中的概念,即每次的输入相同输出的结果必然相同,个人认为这个编程里面要比面想对象的编程对应用更加的安全,也方便团队的合作和代码的调试。

Immutable的缺点:

1.增加学习成本

使用immutable和使用原生的js对象有很大的不同,比如在immutable中就不能够使用解构赋值,和对象运算符,取而代之的是使用immutable中get,set等api。但是immutable的常用API通过官网学习也是很简单掌握的。

2.侵入性强

当我们使用第三方组件库的时候将不得不将immutable通过toJS方法将其转为原生的JS对象。

3.容易与原生对象混淆

在实际开发过程中通常有些人会把immutable对象和原生JS对象用混,这个问题其实是可以通过命名规则的方法来解决,例如immutable对象我们可以给其加一个$前缀:

let $obj = Immutable.fromJS({a: 1}) // immutable对象
let obj = {a: 1} // 原生对象

但是还是建议在程序用如果引用了immutable就尽量不要使用原生的JS对象,除非特殊的情况不得已将immutable对象转为原生JS进行操作。因为两种对象互相转换其实还是挺耗费性能的,尽量避免这种转换。

总结:

当项目变得复杂时,每一次action对于生成的新state都会消耗一定的性能,所以为了防止不符合预期的re-render,减少不必要的render提高react的性能,可以通过immutable来最大实现符合我们预期的re-render。由于侵入性较强,新项目引入比较容易,老项目迁移需要评估迁移。对于一些提供给外部使用的公共组件,最好不要把 Immutable 对象直接暴露在对外接口中。

相关文章

网友评论

    本文标题:Immutable简介及在React应用中的价值

    本文链接:https://www.haomeiwen.com/subject/anxgiqtx.html