React性能优化之使用 ImmutableJS 不可变数据

3,583 阅读3分钟

众所周知,组件化是 React 的最大特性,所以我们需要关注组件的优化。

一、问题:不该重新渲染的子组件跟着别人又 render 了

我们先来看一段包含子组件的代码:

import React, { Component } from 'react';
import ColorShow from "./ColorShow";
class Example extends Component {
    constructor(props){
        super(props);
        
        this.state = {
            count:0
        }
    }
    btnClick = () => {
        this.setState({count:this.state.count+1})
    }
    render() { 
        return ( 
            <div>
                <h2>{`this is Immutable example , you click ${this.state.count} times`}</h2>
                <button onClick={this.btnClick}>click</button>
                <ColorShow />
            </div>
         );
    } 
}
export default Example;

子组件 ColorShow 的代码如下

import React, { Component } from 'react';
class ColorShow extends Component {
    
    componentDidUpdate (){
        console.log("ColorShow componentDidUpdate");
    }
    render() { 
        return ( 
            <div >
                <h2>i am show area</h2>
            </div>
         );
    }
}
export default ColorShow;

我们在子组件 ColorShow 中的 componentDidUpdate 勾子里打印一些东西,表明在组件重新 render 了。运行如下

如动图可知,我们其实只需要你点击几次的那块区域改变,但是子组件也每次都跟着重新渲染了。这就是我们需要优化的性能问题了,我们要它在该改变的时候,才去重新 render 的时候。

二、使用 PureComponent 优化

React15.3 增加 Component 的升级版 PureComponentPureComponent 不需要我们去实现 shouldComponentUpdate 函数,它会浅比较的对比 prop 和 state 来决定组件是否需要 render。而浅比较的弊端是当 props 的结构比较复杂时,如多重对象嵌套,深层次对象的改变,这个浅比较就无法比较出来,从而阻止了组件的重新渲染。所以,PureComponent 并不能完全帮助我们在复杂数据时作出正确决策。

三、使用 Component 中的 shouldComponentUpdate 函数优化。

在组件中的 shouldComponentUpdate 函数返回 true 或 false,决定着组件是否重新 render,这就要对前后数据进行深度遍历比较,在数据的结构复杂时,这样很消耗性能。所以我们使用不可变数据 Immutable,引入 Facebook 打造了三年的 immutable.js。现在,我们来看下对比下深度遍历和不可变数据之间的性能比较。 我们会随机生成数据,使用 lodash 中 isEqual 函数进行深度遍历,看代码

//生成随机数据
let map = new Map();
for (let i = 0; i < 8000; i++) {
    map.set(Math.random(), Math.random());
}
let map1 = new Map();
for (let i = 0; i < 8000; i++) {
    map1.set(Math.random(), Math.random());
}

const _ = require("lodash");

//使用lodash进行深度遍历比较 map 和 map1
(function test () {
    console.time("isEqual");
    console.log(_.isEqual(map,map1));
    console.timeEnd("isEqual");
}());

执行,比较程序的运行时间在 30ms 多。

接着我们看看使用 immutable.js 库来比较的情况,先看代码

//生成随机数据
let map = new Map();
for (let i = 0; i < 8000; i++) {
    map.set(Math.random(), Math.random());
}
let map1 = new Map();
for (let i = 0; i < 8000; i++) {
    map1.set(Math.random(), Math.random());
}
const {is,fromJS} = require('immutable');

//生成不可变数据
const i_map = fromJS(map);
const i_map1 = fromJS(map1);

//使用immutable中的is函数 比较两个不可变数据 i_map 和 i_map1
(function test () {
    console.time("immutable_is");
    console.log(is(i_map,i_map1));
    console.timeEnd("immutable_is");
}());

我们先使用 fromJS 构建不可变数据,通过 is 函数比较。执行,函数执行时间在 3ms 左右。

组件能够快速准确的做出是否重新渲染的决定,有利于提升我们整个页面的性能。

四、总结

不可变数据 ImmutableReact 性能的优化,这完全得益于 Immutable 的结构共享。我们目前仅从组件的角度来体现,Immutable 还有更多更好的东西值得我们研究和使用,比如和与 Redux 的搭配使用,具体大家可以参照阿里大佬camsong写的这篇详解

参考:
Immutable 详解及 React 中实践
immutable-js