React全家桶之Immutable.js

2,054 阅读4分钟

在 React 开发中 React 强调开发者无时无刻去关注 数据的纯度 无论是类组件中的 state 还是 redux 中的 state

JavaScript 中的对象一般是可变的(Mutable),因为使用了引用赋值,新的对象简单的引用了原始对象,改变新的对象将影响到原始对象。如 foo={a: 1}; bar=foo; bar.a=2 此时 foo.a 也被改成了 2。虽然这样做可以节约内存,但当应用复杂后,这就造成了非常大的隐患,Mutable 带来的优点变得得不偿失。为了解决这个问题,一般的做法是使用 shallowCopy(浅拷贝)或 deepCopy(深拷贝)来避免被修改,但这样做造成了 CPU 和内存的浪费。

上述问题等同于在A组件中修改公共状态 在B组件中同样也被修改了

遇到这种问题如何解决呢?

ES6中 (...)扩展运算符 & Object.assign ES5中 Object.freeze()

但是这种浅层拷贝有什么问题呢? 答:一般没有 除非对象过于庞大 才会带来性能问题以及内存浪费

可不可以做的更好?

可以! 使用 ImmutableJS 进行性能优化

Immutable 优点

  1. Immutable 降低了 Mutable 带来的复杂度

  2. 节省内存

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

  1. 拥抱函数式编程

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

image.png

Immutable 本意为不可改变的 这个库弥补了 Javascript 没有不可变数据结构的问题 那他是如何做到不可变的呢?

ImmutableJS核心概念就一个 只要修改了对象,就会返回一个新的对象,旧的对象不会发生改变;且新的对象会尽可能利用原来对象的结构 即结构共享

同时利用了数据结构中的16叉树最大程度保证了树的深度问题 减少更新的结构

ImmutableJS 基本使用

ImutableJS常见API

ImutableJS数据结构

  • Map:键值对集合,对应于 Object,ES6 也有专门的 Map 对象
  • List:有序可重复的列表,对应于 Array
  • Set:无序且不可重复的列表

JavaScript和ImmutableJS直接的转换

  • 对象转换成Immutable对象:Map

  • 数组转换成Immutable数组:List

  • 深层转换:fromJS

  • Immutable类型转换js:toJS()

ImmutableJS的基本操作

  • 修改数据:set

  • 获取数据:get

 const im = Immutable;
     const info = {
      name: 'xiaoming',
      age: 28,
      friends: {
        name: 'xiaohong',
        age: 32
      }
    }

    // 对上方对象进行转化时
    const infoIm = im.Map(info)
    const obj = infoIm;
    // 修改时需要用set进行修改 同时这里不会主动改这里对infoIm 而是生成一个新的对象
    const infoIm2 = infoIm.set("name", '小明')
    console.log(infoIm2);
    // 通过get来取数据
    console.log(obj.get('name'));
    console.log(infoIm2.get('name'));
    
    // list
    const num = ['1', '2', '3', '4'];

    const numIm = im.List(num)

    console.log(numIm);

    const arr = numIm.set(0, 11)
    console.log(numIm.get(0));
    console.log(arr.get(0));

    console.log(infoIm.get('friends')); // {name: "xiaohong", age: 32} 普通对象

    // 传入js对象或数组 转化为 Immutable类型
    const infoIm3 = im.fromJS(info)
    console.log(infoIm3.get('friends'));

ImutableJS 结合 Redux使用

yarn add immutable

由于Redux有且仅有一个store 但是可以拆分为多个reducer 我用一个其中一个reducer进行举例

修改前的Reducer


    import { CONSTANTS } from "./constants";
    // 创建默认状态
    const defaultState = Map{
    topBanners: [],
    };
    const reducer = (state = defaultState, action) => {
        switch (action.type) {
        case CONSTANTS:
          return {...state, action.data}
        default:
          return state;
        }};
        
    export default reducer;

结合ImutableJS后的Reducer

    import { Map } from "immutable";
    import { CONSTANTS } from "./constants";
    // 将 defaultState 转换成 Map类型
    const defaultState = Map({
    topBanners: [],
    });
    const reducer = (state = defaultState, action) => {
        switch (action.type) {
        case CONSTANTS:
          // 调用Map对象中的set方法
          return state.set("data", action.data);
        default:
          return state;
        }};
        
    export default reducer;

在主reducer进行合并

这里要说明一点: immutablejs里面的Map进行包裹 而 Redux 中的 combineReducers 源码里面实现过程是通过Object.key方式将每个模块的小reduer进行传递 如果这时候拿Map进行包裹肯定不是Map想要的数据结构

yarn add redux-immutable

    import { combineReducers } from "redux-immutable";
    import { reducer as recommend } from"@v/xxx/components/xxxxx/store/index";
    const mergeReducer = combineReducers({
        recommend,
    });
    export default mergeReducer;

总结

Immutable 可以给应用带来极大的性能提升,但是由于侵入性较强 是否使用必须考虑项目复杂程度