react性能优化

97 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第3天,点击查看活动详情

上一篇文章我们讲的是关于state更新的性能优化,通过shouldComponentUpdate来进行判断组件是否需要进行重新的render,减少不必要的渲染达到性能优化的目的。

需求描述

当有子父组件出现的时候,我们的子组件就有可能接受到props,有的时候我们希望通过在子组件中操作改变父组件的状态,这个时候我们一般就是通过给子组件传递回调函数来进行我们的操作,很明显这样的回调函数是不会改变的,那么子组件就不应该因为接受这个props后发生重新的渲染,但是实际上是会进行更改的。

场景模拟

父组件

import React, { useState } from "react";
import { useCallback } from "react";
import Test from "./Test";
const App = () => {
  const [count, setCount] = useState(0);
  const onClickHandle = () => {
    setCount((count) => count + 1);
  };
  return (
    <div>
      <h1>{count}</h1>
      <Test onClickHandle={onClickHandle} />
    </div>
  );
};

export default App;

子组件

import React, { Component } from 'react';

class Test extends Component {
  render() {
    return (
    <div>
      <button onClick={this.props.onClickHandle}>点击加一</button>
      <h1>{Math.random()}</h1>
    </div>
    );
  }
}

export default Test;

运行效果

1.gif

每次的运行子组件的随机数都进行了改变,说明了其都进行了重新的渲染。借助我们上一篇的文章我们想到是不是让组件继承PureComponent就可以了呢(或者重写shouldComponentUpdate)?这样子就可以开启浅比较,子组件就不会重写渲染了,那我们看看效果:

1.gif

很明显还是没有达到我们想要的效果。其实也很好理解,浅比较是类似Object.is()的过程,对于引用类型,源码部分只是处理了对象和数组,并没有对函数进行处理,而每一次的更新父组件的函数地址都进行了改变,所以在比较的过程中一旦地址不同就认为是新的props了,所以子组件也就进行了更新。

解决问题

得知原理之后我们就可以很方便的想到解决的办法了;

-1首先我们可以重写shouldComponentUpdate方法:

子组件更改如下

import React, { Component } from 'react';

class Test extends Component {
  shouldComponentUpdate(nextProps,nextState){
    return nextProps.onClickHandle.toString()!==this.props.onClickHandle.toString()
  }
  render() {
    return (
    <div>
      <button onClick={this.props.onClickHandle}>点击加一</button>
      <h1>{Math.random()}</h1>
    </div>
    );
  }
}

export default Test;

我们只要比较函数的内容,只要内容不变那么就不进行更新,这样就达到了我们想要的效果。

-2第二种方法就是我们想办法让我们的函数地址不进行地址的更改,也就是进行缓存,我们只要把函数在传递的时候进行缓存就可以了,然后子组件再开启浅比较:

父组件

import React, { useState } from "react";
import { useCallback } from "react";
import Test from "./Test";
const App = () => {
  const [count, setCount] = useState(0);
  const onClickHandle = useCallback(() => {
    setCount((count) => count + 1);
  }, []);
  return (
    <div>
      <h1>{count}</h1>
      <Test onClickHandle={onClickHandle} />
    </div>
  );
};

export default App;

子组件

import React, { PureComponent } from 'react';

class Test extends PureComponent {
  render() {
    return (
    <div>
      <button onClick={this.props.onClickHandle}>点击加一</button>
      <h1>{Math.random()}</h1>
    </div>
    );
  }
}

export default Test;

这样每次在比较的时候发信啊我们的地址没有改变那么子组件就不会重新进行渲染也同样达到了我们的目的。

其他

如果子组件是函数式组件怎么办?这样继承和生命周期的方法都没有用了。解决方法也是很简单,只要我们给子组件包裹一层memo这样就开启了浅比较达到一样的效果了:

import React,{memo} from 'react';

const Test = ({onClickHandle}) => {
  return (
    <div>
      <button onClick={onClickHandle}>点击加一</button>
      <h1>{Math.random()}</h1>
    </div>
  );
}

export default memo(Test);

react给我们提供了很多性能优化的API只有真正的知道了他的实现原理使用起来才会游刃有余。