Aaaaash / blog

✍️不定期断更
108 stars 9 forks source link

利用PureComponent+ImmutableJS实现Pure-render #6

Open Aaaaash opened 6 years ago

Aaaaash commented 6 years ago

利用ImmutableJS实现Pure-Render

PureRender是react应用中最常见的优化方式之一,顾名思义是纯·渲染,React的核心思想可以用一个表达式来概括

view = f(model)

这个很简单的表达式阐述了一个最基本的思想,数据的更新触发视图的更新,如果把它看做一个纯函数,那么给定相同的输入必定得到相同的输出,简而言之就是如果state&props没有改变,理论上讲组件不会重新渲染

React生命周期有一个函数shouldComponentUpdate,看名字就知道这个函数决定了组件要不要更新(重新渲染),默认情况下这个函数始终返回true

但是过多的rerender势必会引起性能问题,所以在必要的情况下开发者需要自己手动实现shouldComponentUpdate:

shouldComponentUpdate(nextProps, nextState) {
  return this.props.value !== nextProps.value;
}

事实上在较新版本的React中内置了一个已经实现shouldComponentUpdate方法的类,叫做PureComponent,使用时只要将原先的Component替换为PureComponent即可

import react, { PureComponent } from 'react';
class MyPage extends PureComponent {
  // your code
}

然而不管是上面那个简单的例子,还是PureComponent,它们实现的方式很简单,都是浅比较

对于基本数据类型,只需要对比值,而引用类型则只对比引用地址,试想一下如果有一个长度为50+的数组,单纯的!==浅对比就完全没有任何用处了,因为数组是引用类型,每次传来新的数组都是不同的引用,始终还是返回true,但是深对比带来的开销更大,到底如何取舍?

答案是ImmutableData

什么是ImmutableData?

ImmutableData(不可变数据)就是指一旦创建就不能被改变的数据 上对ImmutableData的任何修改都会返回一个新的immutable对象

Immutablejs实现了List、Map等常用数据类型,分别对应js中的数组和对象

简单来说,当对一个immutabledata进行增删改操作时,并不会修改原本的数据,而是生成了新的immutable对象,如果没有任何修改则返回原对象

基本用法可参考官方文档

React中怎么用?

可以将组件state中的数据转为immutabledata,也可以将redux的state转为immutabledata

// state
import React from 'react';
import { fromJS } from 'immutable';

class Dmoe extends PureComponent {
  state = {
    someDeepData: fromJS({
      name: 'misaka',
      age: 20,
    }),
  }

  handleChangeAge = () => {
    const { someDeepData } = this.state;
    const prevAge = someDeepData.get('age');
    this.setState({
      someDeepData: someDeepData.set('age', prevAge - 1),
    });
  }

  render() {
    const { someDeepData } = this.state;
    return (<div>
      <h1>{someDeepData.get('name')}</h1>
      <button onClick={this.handleChangeAge}>-1s</button>
    </div>);
  }
}

CodeSandbox在线示例

例如一个父子组件嵌套,父组件数据改变导致自身rerender从而引发子组件一起rerender,这种情况使用ImmutableData + PureComponent则可以很好的避免子组件的重复渲染

class Child extends PureComponent {
  render() {
    const { info } = this.props;
    console.log("render");
    return (
      <div>
        <h1>my name is {info.name}</h1>
        <p>i am {info.age} years old!</p>
      </div>
    );
  }
}

class Parent extends PureComponent {
  state = {
    info: fromJS({
      name: "misaka",
      age: 10
    }),
    age: 20
  };

  handleChangeAge = () => {
    const { age } = this.state;
    this.setState({
      age: age + 1
    });
  };
  render() {
    const { info, age } = this.state;
    return (
      <div>
        <Child info={info} />
        I am Sakura, {age} years old. this is my child!
        <button onClick={this.handleChangeAge}>+1s</button>
      </div>
    );
  }
}

CodeSanBox在线示例

refs:

hhhuangqiong commented 6 years ago

React怎么用的两个例子我都有疑问: 1、将组件state中的数据转为immutabledata有啥好处,我们现在不转也运行的很好,什么场景下才需要用到? 2、第二个,即使info没有使用immutable,只改变了age,pureComponent也是比较的出来的啊,child不会重新渲染,因为此时info传下去的值是一样的

Jemair commented 6 years ago

@hhhuangqiong 关于immutable的优势和实现原理 可以看下这个 https://www.youtube.com/watch?v=Wo0qiGPSV-s&t=4s 、通俗来讲就是在修改复杂数据类型的时候会返回一个新的对象而不是直接在原本的对象上修改 避免造成数据混乱
这里有一个用immutable实现的五子棋 https://github.com/Jemair/fiveGo immutable的优势在todo 和 redo 里体现的很充分