一、是什么

Immutable,不行改动的,在计算机中,即指一旦创立,就不能再被更改的数据

Immutable目标的任何修正或增加删除操作都会返回一个新的Immutable目标

Immutable完成的原理是Persistent Data Structure(持久化数据结构):

  • 用一种数据结构来保存数据
  • 当数据被修正时,会返回一个目标,可是新的目标会尽可能的运用之前的数据结构而不会对内存造成浪费

也便是运用旧数据创立新数据时,要保证旧数据一起可用且不变,一起为了防止deepCopy把所有节点都仿制一遍带来的性能损耗,Immutable运用了Structural Sharing(结构同享)

假如目标树中一个节点发生变化,只修正这个节点和受它影响的父节点,其它节点则进行同享

如下图所示:

闲着没事,来研讨下Immutable

二、怎么运用

运用Immutable目标最首要的库是immutable.js

immutable.js 是一个完全独立的库,不管基于什么框架都能够用它

其出现场景在于补偿 Javascript 没有不行变数据结构的问题,经过 structural sharing来解决的性能问题

内部供给了一套完整的 Persistent Data Structure,还有许多易用的数据类型,如CollectionListMapSetRecordSeq,其中:

  • List: 有序索引集,相似 JavaScript 中的 Array
  • Map: 无序索引集,相似 JavaScript 中的 Object
  • Set: 没有重复值的集合

首要的办法如下:

  • fromJS():将一个js数据转换为Immutable类型的数据
const obj = Immutable.fromJS({a:'123',b:'234'})
  • toJS():将一个Immutable数据转换为JS类型的数据
  • is():对两个目标进行比较
import { Map, is } from 'immutable'
const map1 = Map({ a: 1, b: 1, c: 1 })
const map2 = Map({ a: 1, b: 1, c: 1 })
map1 === map2   //false
Object.is(map1, map2) // false
is(map1, map2) // true
  • get(key):对数据或目标取值
  • getIn([]) :对嵌套目标或数组取值,传参为数组,表明位置
let abs = Immutable.fromJS({a: {b:2}});
abs.getIn(['a', 'b']) // 2
abs.getIn(['a', 'c']) // 子级没有值
let arr = Immutable.fromJS([1 ,2, 3, {a: 5}]);
arr.getIn([3, 'a']); // 5
arr.getIn([3, 'c']); // 子级没有值
  • setIn([]):对变量进行赋值
  • getIn([]):对嵌套数组取值

如下例子:运用办法如下:

import Immutable from 'immutable';
foo = Immutable.fromJS({a: {b: 1}});
bar = foo.setIn(['a', 'b'], 2);   // 运用 setIn 赋值
console.log(foo.getIn(['a', 'b']));  // 运用 getIn 取值,打印 1
console.log(foo === bar);  //  打印 false

假如换到原生的js,则对应如下:

let foo = {a: {b: 1}};
let bar = foo;
bar.a.b = 2;
console.log(foo.a.b);  // 打印 2
console.log(foo === bar);  //  打印 true

三、在React中运用

运用Immutable能够给React运用带来性能的优化,首要体现在减少烘托的次数

在做react性能优化的时分,为了防止重复烘托,咱们会在shouldComponentUpdate()中做对比,当返回true履行render办法

Immutable经过is办法则能够完成对比,而无需像相同经过深度比较的方法比较

在运用redux过程中也能够结合Immutable,不运用Immutable前修正一个数据需求做一个深复制

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 });
  }
}

运用 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'));
  }

同理,在redux中也能够将数据进行fromJS处理

import * as constants from './constants'
import {fromJS} from 'immutable'
const defaultState = fromJS({ //将数据转化成immutable数据
    home:true,
    focused:false,
    mouseIn:false,
    list:[],
    page:1,
    totalPage:1
})
export default(state=defaultState,action)=>{
    switch(action.type){
        case constants.SEARCH_FOCUS:
            return state.set('focused',true) //更改immutable数据
        case constants.CHANGE_HOME_ACTIVE:
            return state.set('home',action.value)
        case constants.SEARCH_BLUR:
            return state.set('focused',false)
        case constants.CHANGE_LIST:
            // return state.set('list',action.data).set('totalPage',action.totalPage)
            //merge功率更高,履行一次改动多个数据
            return state.merge({
                list:action.data,
                totalPage:action.totalPage
            })
        case constants.MOUSE_ENTER:
            return state.set('mouseIn',true)
        case constants.MOUSE_LEAVE:
            return state.set('mouseIn',false)
        case constants.CHANGE_PAGE:
            return state.set('page',action.page)
        default:
            return state
    }
}