React核心开发团队一直都努力地让React变得更快。在React中可以用来优化组件性能的方法大概有以下几种:
- 组件懒加载(React.lazy(...)和
) - Pure Component
- shouldComponentUpdate(...){...}生命周期函数
本文还会介绍React16.6加入的另外一个专门用来优化函数组件(Functional Component)性能的方法: React.memo。
无用的渲染
组件是构成React视图的一个基本单元。有些组件会有自己本地的状态(state), 当它们的值由于用户的操作而发生改变时,组件就会重新渲染。在一个React应用中,一个组件可能会被频繁地进行渲染。这些渲染虽然有一小部分是必须的,不过大多数都是无用的,它们的存在会大大降低我们应用的性能。
看下面这个例子:
import React from 'react';
class TestC extends React.Component {
constructor(props) {
super(props);
this.state = {
count: 0
}
}
componentWillUpdate(nextProps, nextState) {
console.log('componentWillUpdate')
}
componentDidUpdate(prevProps, prevState) {
console.log('componentDidUpdate')
}
render() {
return (
{this.state.count}
);
}
}
export default TestC;
TestC组件有一个本地状态count,它的初始值是0(state = {count: 0})。当我们点击Click Me按钮时,count的值被设置为1。这时候屏幕的数字将会由0变成1。当我们再次点击该按钮时,count的值还是1, 这时候TestC组件不应该被重新渲染,可是现实是这样的吗?
为了测试count重复设置相同的值组件会不会被重新渲染, 我为TestC组件添加了两个生命周期函数: componentWillUpdate和componentDidUpdate。componentWillUpdate方法在组件将要被重新渲染时被调用,而componentDidUpdate方法会在组件成功重渲染后被调用。
在浏览器中运行我们的代码,然后多次点击Click Me按钮,你可以看到以下输出:
我们可以看到'componentWillUpdate'和'componentWillUpdate'在每次我们点击完按钮后,都会在控制台输出来。所以即使count被设置相同的值,TestC组件还是会被重新渲染,这些就是所谓的无用渲染。
Pure Component/shouldComponentUpdate
为了避免React组件的无用渲染,我们可以实现自己的shouldComponentUpdate生命周期函数。
当React想要渲染一个组件的时候,它将会调用这个组件的shouldComponentUpdate函数, 这个函数会告诉它是不是真的要渲染这个组件。
如果我们的shouldComponentUpdate函数这样写:
shouldComponentUpdate(nextProps, nextState) {
return true
}
其中各个参数的含义是:
- nextProps: 组件将会接收的下一个参数props
- nextProps: 组件的下一个状态state
因为我们的shouldComponentUpdate函数一直返回true,这就告诉React,无论何种情况都要重新渲染该组件。
可是如果我们这么写:
shouldComponentUpdate(nextProps, nextState) {
return false
}
因为这个方法的返回值是false,所以React永远都不会重新渲染我们的组件。
因此当你想要React重新渲染你的组件的时候,就在这个方法中返回true,否则返回false。现在让我们用shouldComponentUpdate重写之前的TestC组件:
import React from 'react';
class TestC extends React.Component {
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 (
{ this.state.count }
);
}
}
export default TestC;
我们在TestC组件里添加了shouldComponentUpdate方法,判断如果现在状态的count和下一个状态的count一样时,我们返回false,这样React将不会进行组件的重新渲染,反之,如果它们两个的值不一样,就返回true,这样组件将会重新进行渲染。
再次在浏览器中测试我们的组件,刚开始的界面是这样的:
这时候,就算我们多次点击Click Me按钮,也只能看到两行输出:
componentWillUpdate
componentDidUpdate
因为第二次点击Click Me按钮后count值一直是1,这样shouldComponentUpdate一直返回false,所以组件就不再被重新渲染了。
那么如何验证后面state的值发生改变,组件还是会被重新渲染呢?我们可以在浏览器的React DevTools插件中直接对TestC组件的状态进行更改。具体做法是, 在Chrome调试工具中点击React标签,在界面左边选中TestC组件,在界面的右边就可以看到其状态state中只有一个键count,且其值是1:
然后让我们点击count的值1,将其修改为2,然后按回车键:
你将会看到控制台有以下输出:
componentWillUpdate
componentDidUpdate
componentWillUpdate
componentDidUpdate
state的count被改变了,组件也被重新渲染了。
现在让我们使用另外一种方法PureComponent来对组件进行优化。
React在v15.5的时候引入了Pure Component组件。React在进行组件更新时,如果发现这个组件是一个PureComponent,它会将组件现在的state和props和其下一个state和props进行浅比较,如果它们的值没有变化,就不会进行更新。要想让你的组件成为Pure Component,只需要extends React.PureComponent即可。
让我们用PureComponent去改写一下我们的代码吧:
import React from 'react';
class TestC extends React.PureComponent {
constructor(props) {
super(props);
this.state = {
count: 0
}
}
componentWillUpdate(nextProps, nextState) {
console.log('componentWillUpdate')
}
componentDidUpdate(prevProps, prevState) {
console.log('componentDidUpdate')
}
render() {
return (
{ this.state.count }



