资讯专栏INFORMATION COLUMN

React-redux进阶之Immutable.js

孙淑建 / 257人阅读

摘要:的优势保证不可变每次通过操作的对象都会返回一个新的对象丰富的性能好通过字典树对数据结构的共享的问题与原生交互不友好通过生成的对象在操作上与原生不同,如访问属性,。

Immutable.js

Immutable的优势

1. 保证不可变(每次通过Immutable.js操作的对象都会返回一个新的对象)  
2. 丰富的API  
3. 性能好 (通过字典树对数据结构的共享)



Immutable的问题

1. 与原生JS交互不友好 (通过Immutable生成的对象在操作上与原生JS不同,如访问属性,myObj.prop1.prop2.prop3 => myImmutableMap.getIn([‘prop1’, ‘prop2’, ‘prop3’])。另外其他的第三方库可能需要的是一个普通的对象)  
2. Immutable的依赖性极强 (一旦在代码中引入使用,很容易传播整个代码库,并且很难在将来的版本中移除)  
3. 不能使用解构和对象运算符 (相对来说,代码的可读性差)  
4. 不适合经常修改的简单对象 (Immutable的性能比原生慢,如果对象简单,并且经常修改,不适合用)  
5. 难以调试 (可以采用 Immutable.js Object Formatter扩展程序协助)  
6. 破坏JS原生对象的引用,造成性能低下 (toJs每次都会返回一个新对象)


原生Js遇到的问题

原生Js遇到的问题

// 场景一
var obj = {a:1, b:{c:2}};
func(obj);
console.log(obj)  //输出什么??

// 场景二
var obj = ={a:1};
var obj2 = obj;
obj2.a = 2;
console.log(obj.a);  // 2
console.log(obj2.a);  // 2

代码来源:https://juejin.im/post/5948985ea0bb9f006bed7472
// ajax1
this.props.a = {
  data: 1,
}
// ajax2
nextProps.a = {
  data: 1,
}
//shouldComponentUpdate()
shallowEqual(this.props, nextProps) // false
// 数据相同但是因为引用不同而造成不必要的re-rederning
由于Js中的对象是引用类型的,所以很多时候我们并不知道我们的对象在哪里被操作了什么,而在Redux中,因为Reducer是一个纯函数,每次返回的都是一个新的对象(重新生成对象占用时间及内存),再加上我们使用了connect这个高阶组件,官方文档中虽然说react-redux做了一些性能优化,但终究起来,react-redux只是对传入的参数进行了一个浅比较来进行re-redering(为什么不能在mapStateToProps中使用toJs的原因)。再进一步,假如我们的state中的属性嵌套了好几层(随着业务的发展),对于原来想要的数据追踪等都变得极为困难,更为重要的是,在这种情况下,我们一些没有必要的组件很可能重复渲染了多次。  


总结起来就是以下几点(问题虽少,但都是比较严重的):

1. 无法追踪Js对象  
2. 项目复杂时,reducer生成新对象性能低  
3. 只做浅比较,有可能会造成re-redering不符合预期(多次渲染或不更新)


为什么不使用深比较

或许有人会疑惑,为什么不使用深比较来解决re-redering的问题,答案很简单,因为消耗非常巨大~  
想象一下,如果你的参数复杂且巨大, 对每一个进行比较是多么消耗时间的一件事~  


使用Immutable解决问题

项目复杂后, 追踪困难

使用Immutable之后,这个问题自然而然就解决了。所谓的追踪困难,无非就是因为对象是mutable的,我们无法确定它到底何时何处被改变,而Immutable每次都会保留原来的对象,重新生成一个对象,(与redux的纯函数概念一样)。但也要注意写代码时的习惯:

// javascript
const obj = { a: 1 }
function (obj) {
  obj.b = 2
  ...
}

// Immutable
const obj = Map({ a : 1 })
function (obj) {
  const obj2 = obj.set({ "b", 2 })
}


reducer生成新对象性能差
当项目变得复杂时,每一次action对于生成的新state都会消耗一定的性能,而Immutable.js在这方面的优化就很好。或许你会疑惑为什么生成对象还能优化?请往下看~

在前面就讲到,Immutable是通过字典树来做==结构共享==的


(图片来自网络)

这张图的意思就是

immutable使用先进的tries(字典树)技术实现结构共享来解决性能问题,当我们对一个Immutable对象进行操作的时候,ImmutableJS会只clone该节点以及它的祖先节点,其他保持不变,这样可以共享相同的部分,大大提高性能。


re-rendering不符合预期

其实解决这个问题是我们用Immutable的主要目的,先从浅比较说起
浅比较引起的问题在这之前已经讲过,事实上,即使Immutable之后,connect所做的依然是浅比较,但因为Immutable每次生成的对象引用都不同,哪怕是修改的是很深层的东西,最后比较的结果也是不同的,所以在这里解决了第一个问题,==re-rendering可能不会出现==。
但是, 我们还有第二个问题, ==没必要的re-rendering==,想要解决这个问题,则需要我们再封装一个高阶组件,在这之前需要了解下Immutable的 is API

// is()   判断两个immutable对象是否相等
immutable.is(imA, imB);

这个API有什么不同, ==这个API比较的是值,而不是引用==,So: 只要两个值是一样的,那么结果就是true

const a = Immutable.fromJS({
  a: {
    data: 1,
  },
  b: {
    newData: {
      data: 1
    }
  }
})
const target1 = a.get("a")
const target2 = a.getIn(["b", "newData"])
console.log(Immutable.is(target1, target2)) //is比较的依据就是每个值的hashcode
// 这个hashcode就相当于每个值的一个ID,不同的值肯定有不同的ID,相同的ID对应着的就是相同的值。

也就是说,对于下面的这种情况, 我们可以不用渲染

// ajax1
this.props.a = {
  data: 1,
}
// ajax2
nextProps.a = {
  data: 1,
}
//shouldComponentUpdate()
Immutable.is(this.props, nextProps) // true

最后, 我们需要封装一个高阶组件来帮助我们统一处理是否需要re-rendering的情况

//baseComponent.js   component的基类方法

import React from "react";
import {is} from "immutable";

class BaseComponent extends React.Component {
    constructor(props, context, updater) {
        super(props, context, updater);
    }

    shouldComponentUpdate(nextProps, nextState) {
        const thisProps = this.props || {};
        const thisState = this.state || {};
        nextState = nextState || {};
        nextProps = nextProps || {};

        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 (!is(thisState[key], nextState[key])) {
                return true;
            }
        }
        return false;
    }
}

export default BaseComponent;

代码来源链接:https://juejin.im/post/5948985ea0bb9f006bed7472


使用Immutable需要注意的点

使用Immutable需要注意的点

1. 不要混合普通的JS对象和Immutable对象 (不要把Imuutable对象作为Js对象的属性,或者反过来)  
2. 对整颗Reudx的state树作为Immutable对象  
3. 除了展示组件以外,其他地方都应该使用Immutable对象 (提高效率,而展示组件是纯组件,不应该使用) 
4. 少用toJS方法 (一个是因为否定了Immutable,另外则是操作非常昂贵)  
5. 你的Selector应该永远返回Immutable对象 (即mapStateToProps,因为react-redux中是通过浅比较来决定是否re-redering,而使用toJs的话,每次都会返回一个新对象,即引用不同)


通过高阶组件,将Immutable对象转为普通对象传给展示组件

1. 高阶组件返回一个新的组件,该组件接受Immutable参数,并在内部转为普通的JS对象  
2. 转为普通对象后, 新组件返回一个入参为普通对象的展示组件
import React from "react"
import { Iterable } from "immutable"

export const toJS = WrappedComponent => wrappedComponentProps => {
  const KEY = 0
  const VALUE = 1

  const propsJS = Object.entries(wrappedComponentProps).reduce(
    (newProps, wrappedComponentProp) => {
      newProps[wrappedComponentProp[KEY]] = Iterable.isIterable(
        wrappedComponentProp[VALUE]
      )
        ? wrappedComponentProp[VALUE].toJS()
        : wrappedComponentProp[VALUE]
      return newProps
    },
    {}
  )

  return 
}
import { connect } from "react-redux"

import { toJS } from "./to-js"
import DumbComponent from "./dumb.component"

const mapStateToProps = state => {
  return {
    // obj is an Immutable object in Smart Component, but it’s converted to a plain
    // JavaScript object by toJS, and so passed to DumbComponent as a pure JavaScript
    // object. Because it’s still an Immutable.JS object here in mapStateToProps, though,
    // there is no issue with errant re-renderings.
    obj: getImmutableObjectFromStateTree(state)
  }
}
export default connect(mapStateToProps)(toJS(DumbComponent))
参考


Immutable.js 以及在 react+redux 项目中的实践

Using Immutable.JS with Redux

不变应万变-Immutable优化React

React-Redux分析

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/101588.html

相关文章

  • React-Redux进阶(像VUEX一样使用Redux)

    摘要:前言是一个非常实用的状态管理库,对于大多数使用库的开发者来说,都是会接触到的。在使用享受其带来的便利的同时,我们也深受其问题的困扰。只支持同步,让状态可预测,方便测试。粗暴地级联式刷新视图使用优化。 前言 Redux是一个非常实用的状态管理库,对于大多数使用React库的开发者来说,Redux都是会接触到的。在使用Redux享受其带来的便利的同时, 我们也深受其问题的困扰。 redux...

    levius 评论0 收藏0
  • 一个基于React整套技术栈+Node.js的前端页面制作工具

    摘要:是一个前端页面制作工具,方便产品,运营和视觉的同学迅速开发简单的前端页面,从而可以解放前端同学的工作量。支持恢复现场功能关闭页面配置不丢失支持操作。提供了一个方法,用于的拆分。就是发出的通知,表示应该要发生变化了。 pagemaker是一个前端页面制作工具,方便产品,运营和视觉的同学迅速开发简单的前端页面,从而可以解放前端同学的工作量。此项目创意来自网易乐得内部项目nfop中的page...

    ermaoL 评论0 收藏0
  • 2017-06-23 前端日报

    摘要:前端日报精选大前端公共知识梳理这些知识你都掌握了吗以及在项目中的实践深入贯彻闭包思想,全面理解闭包形成过程重温核心概念和基本用法前端学习笔记自定义元素教程阮一峰的网络日志中文译回调是什么鬼掘金译年,一个开发者的好习惯知乎专 2017-06-23 前端日报 精选 大前端公共知识梳理:这些知识你都掌握了吗?Immutable.js 以及在 react+redux 项目中的实践深入贯彻闭包思...

    Vixb 评论0 收藏0
  • redux常见问题答疑

    摘要:为什么使用的核心是将组件化,由数据驱动的展现。仅仅使用进行开发的痛点组件嵌套层级深,回调地狱。遵守容器组件与展示组件分离的原则。 为什么使用redux React的核心是将UI组件化,由数据驱动UI的展现。但是如何管理数据模型、组件与数据模型之间的通信,react并没有很好的解决方案。Redux由flux演变而来,同时简化了Flux的流程。 仅仅使用react进行开发的痛点 组件嵌套...

    wing324 评论0 收藏0
  • 前端进阶(11) - js 数据结构类型扩展:immutable-js

    摘要:数据结构类型扩展相对之类的强类型语言,有一点很大的区别就是,数据结构只有与,并且都是动态可变的,而有等数据结构。所以,为了能在中也使用这些数据结构,就应运而生。扩充了中的不可变集合,即一旦创建就不能改变的数据类型。 js 数据结构类型扩展:immutable-js 相对 java、.net 之类的强类型语言,js 有一点很大的区别就是,数据结构只有 array 与 object,并且都...

    BLUE 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<