React-redux进阶之Immutable.js

栏目: JavaScript · 发布时间: 5年前

内容简介:<br/>Immutable的问题<br/>

Immutable.js

Immutable的优势

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

<br/>

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每次都会返回一个新对象)

<br/>

原生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中的属性嵌套了好几层(随着业务的发展),对于原来想要的数据追踪等都变得极为困难,更为重要的是,在这种情况下,我们一些没有必要的组件很可能重复渲染了多次。

<br/>

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

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

<br/>

为什么不使用深比较

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

<br/>

使用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 })
}

<br/>

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

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

(图片来自网络)

这张图的意思就是

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

<br/>

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

<br/>

使用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的话,每次都会返回一个新对象,即引用不同)

<br/>

通过高阶组件,将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 <WrappedComponent {...propsJS} />
}
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))

参考

<html>

Immutable.js 以及在 react+redux 项目中的实践 <br/>

Using Immutable.JS with Redux <br/>

不变应万变-Immutable优化React <br/>

React-Redux分析 <br/>

</html>


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

The Little MLer

The Little MLer

Matthias Felleisen、Daniel P. Friedman、Duane Bibby、Robin Milner / The MIT Press / 1998-2-19 / USD 34.00

The book, written in the style of The Little Schemer, introduces instructors, students, and practicioners to type-directed functional programming. It covers basic types, quickly moves into datatypes, ......一起来看看 《The Little MLer》 这本书的介绍吧!

随机密码生成器
随机密码生成器

多种字符组合密码

SHA 加密
SHA 加密

SHA 加密工具

RGB CMYK 转换工具
RGB CMYK 转换工具

RGB CMYK 互转工具