「框架篇」使用 React.memo() 提升react应用性能

无用的渲染

组件构成 了React 视图中的单个单元,这些组件具有状态,该状态对于组件是局部的,当用户操作改变状态值时,组件知道何时重新呈现。有时这些重新渲染可能是必要的,但大多数情况下不需要组件重新渲染,这将影响我们的应用程序的性能。

看这个例子:

import React from 'react';

class Counter extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      count: 0
    }
  }
  componentWillUpdate(nexrProps, nextState) {
    console.log('componentWillUpdate');
  }
  componentDidUpdate(prevProps, prevState) {
    console.log('componentDidUpdate');
  }
  render() {
    return(
      <div >
        {this.state.count}
        <button onClick={()=>this.setState({count: 1})}>Click Me</button>
      </div>

    )
  }  
}

export default Counter;

它有一个状态 count 初始化为 0,当我们点击 Click Me 按钮时,它将计数状态设置为 1,页面将从 0 变为 1。当我们再次单击该按钮时,因为状态没有改变组件应该不会重新渲染。

我们添加了两个生命周期方法来检测当我们将相同的状态设置两次时我们的Counter 组件是否会更新。我添加了 componentWillUpdate,在收到新的props 或 state 时,在渲染之前调用,初始渲染不会调用此方法。并且我还添加了 componentdidUpdate,当组件更新发生后立即调用,初始渲染不会调用此方法。

在浏览器中运行我们的组件并多次点击 Click Me 按钮时,您将看到:

我们看到即使状态相同,控制台每次都会打印 componentWillUpdate 和 componentWillUpdate 组件每次都会重新渲染,这是一种浪费渲染。

shouldComponentUpdate

为了避免在 react 组件中的重复的浪费渲染,我们可以使用 shouldComponentUpdate 生命周期方法。

shouldComponentUpdate 方法,当渲染组件时,React 会对调用此方法,这个方法会向组件提供前进或停止的信号,当来告 React 是否需要渲染组件。

我们可以这样:

shouldComponentUpdate(nextProps, nextState) {
  //return true;  允许重新渲染组件
  //return false; 组件不重新渲染        
}

现在我们使用 shouldComponentUpdate 生命周期方法修改下我们的代码:

import React from 'react';

class Counter extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      count: 0
    }
  }
  componentWillUpdate(nexrProps, nextState) {
    console.log('componentWillUpdate');
  }
  componentDidUpdate(prevProps, prevState) {
    console.log('componentDidUpdate');
  }
  shouldComponentUpdate(nextProps, nextState) {
    if (this.state.count === nextState.count) {
        return false;
    }
    return true;
  }
  render() {
    return(
      <div >
        {this.state.count}
        <button onClick={()=>this.setState({count: 1})}>Click Me</button>
      </div>

    )
  }  
}

export default Counter;

我们在 Conuter 组件中添加了 shouldComponentUpdate 方法,我们检查当前count 值是否等于下一次 nextState.count 的值,如果它们相等,则不重新渲染,返回 false,如果它们不相等则返回 true,因此应该重新渲染以显示新值。

在浏览器中测试它,当点击 Click Me 按钮时,我们看到组件只重新渲染一次:

Pure Component

React在 v15.5 中引入了 Pure Components(纯组件)。它启用了默认的相等检查(change detection)。我们不必将 shouldComponentUpdate 生命周期方法添加到我们的组件中进行状态检测,我们只需要 extends React.PureComponent 将组件变成一个纯组件。

使用 PureComponent 修改我们的 Counter 组件:

import React from 'react';

class Counter extends React.PureComponent {
  constructor(props) {
      super(props);
      this.state = {
          count: 0
      }
  }
  componentWillUpdate(nextProps, nextState) {
      console.log('componentWillUpdate')
  }
  componentDidUpdate(prevProps, prevState) {
      console.log('componentDidUpdate')
  }
  /*shouldComponentUpdate(nextProps, nextState) {
      if (this.state.count === nextState.count) {
          return false
      }
      return true
  }*/
  render() {
    return ( 
      <div> 
        { this.state.count } 
        <button onClick = { () => this.setState({ count: 1 }) }> Click Me </button> 
      </div>
    )
  }
}
export default Counter;

我们注释了 shouldComponentUpdate 生命周期方法。因为React.PureComponent 已经为我们做了同样的操作。

现在我们 在页面中多次单击 Click Me 按钮,我们看到,控制台只打印一次 componentWillUpdate 和 componentDidUpdate。

函数组件(Functional Components)

现在我们看到了如何使用 Pure Components 和 shouldComponentUpdate 生命周期方法优化 class 组件。类组件是 React 的主要组成部分。但是函数也可以在 React 中用作组件,使用函数组件,我们无法使用 state 并且无法提供类似的生命周期方法,函数组件只负责接收 props,渲染DOM,而不关注其他逻辑。

现在我们将 ES6 类组件 Counter 转换为函数组件。

import React from 'react';


const Counter = (props) => {
  console.log(props.count)
  return ( 
    <div>
        {props.count}
        <button>Click Me </button>
    </div>
  )
}

export default Counter;

// App.js 
// <TestC count = {45} />

当页面第一次渲染时,控制台会打印 props 传递过来的 45,但是使用函数编写组件时我们需要注意,如果 prevProps 和当前的 props 相同时,我们如何控制重新渲染呢?解决方案就是使用React.memo()。

React.memo()

React.memo() 是React v16.6 中引入的新功能,是一个更高阶的组件

它的工作方式类似 React.PureComponent,它有助于控制函数组件的重新渲染,这里需要记住 React.memo() 是函数组件,React.PureComponent 类组件。

React.memo 返回了一个具有记忆功能(MemodFuncComponent)的纯组件。在给定相同的 props 的情况下呈现相同的结果。使用 React.memo 通过记忆结果将其封存在一些调用中以提高性能。这意味着 React 将跳过渲染组件的过程,并重用最后渲染的结果。

现在我们使用 React.memo() 修改 Counter 组件,如下所示:

import React from 'react';


let Counter = (props) => {
    console.log(props)
    return ( 
      <div>{props.count}</div>
    )
}
Counter = React.memo(Counter);

export default Counter;

// App.js 
// <TestC count = {45} />

现在 Counter 组件是一个具有记忆功能的组件,如果 prevProps 和当前的 props 相同,则会跳过重新渲染而重用当前的渲染结果。

默认情况下,React.memo() 只会浅显的比较 props 对象中的复杂对象。如果要控制比较,还可以提供自定义比较函数作为第二个参数。

官方的例子:

function MyComponent(props) {
  /* render using props */
}
function areEqual(prevProps, nextProps) {
  /*
  return true if passing nextProps to render would return
  the same result as passing prevProps to render,
  otherwise return false
  */
}
export default React.memo(MyComponent, areEqual);

最后简单的总结一下:

  • React.PureComponent 是纯组件
  • React.memo() 是高阶组件
  • React.PureComponent 是 ES6 类组件
  • React.memo() 是函数组件
  • React.PureComponent 优化 ES6 类组件中的重新渲染
  • React.memo() 优化函数组件的重新渲染

原文发布于微信公众号 - 前端infoQ(webinfoq)

原文发表时间:2019-03-11

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

扫码关注云+社区

领取腾讯云代金券