【React】三种方法实现React性能优化

102 阅读3分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

前言

本文主要内容:了解shouldComponentUpdate()、React.PureComponet以及React.memo()三种防止组件重复渲染的方法

项目预览与分析

直接从console.log中也可以推断出,我们总共定义了四个组件,每个组件调用两次它下一层的组件,比如App组件调用了两次GrandParent组件,GrandParent组件调用了两次Parent组件,以此类推

只不过还在App组件中定义了一个按钮点击函数,用来每次方便地重新渲染App组件

image.png

所以代码也确实如此,App.js中的代码如下

import React, {Component} from "react"
import GrandParent from "./GrandParent"

class App extends Component {
    state = { count: 0 }
    
    increment = () => this.setState(prevState => ({count: prevState.count + 1}))
    
    render() {
        console.log("[GP] [P] [C] [GC] APP just rendered")
        return (
            <div>
                <button onClick={this.increment}>+1</button>
                <h2>{this.state.count}</h2>
                <p>I'm the App component</p>
                <GrandParent />
                <GrandParent />
            </div>
        )    
    }
}

export default App

以GrandParent.js代码为例,如下

import React, {PureComponent} from "react"
import Parent from "./Parent"

class GrandParent extends PureComponent {     
    render() {
        console.log("[👴🏼]   [ ]   [ ]   [ ] rendered")
        return (
            <div>
                <p>I'm a GrandParent Component</p>
                <Parent />
                <Parent />
            </div>
        )
    }
}

export default GrandParent

所以现在的情况就是,只要我们点击"+1",所有的组件都会被依次渲染

image.png

为了优化这种一次渲染所有组件的情况,我们有三种方法。

shouldComponentUpdate()

根据形式也能判断出是class组件的生命周期方法,它的用法是接收将来的props和state与现在的props和state进行比较,如果return false,则表示不要更新;return true,则表示会更新重新render。

所以这里我们可以让GrandParent组件使用这个方法

因为GrandParent组件没有设置状态,所以使用props进行判断

shouldComponentUpdate(nextProps, nextState) {
    if(nextProps.count === this.props.count) {
        return false;
    }
    return true;
}     

可以看到设置了props的count作为判断值,如果相等则返回false,不更新。

于是我们在App.js中传入这个count,count={this.props.count},此时每次点击GrandParent组件都会更新,因为每次传入的count都与上一次不同

那么为了使每次点击不刷新,我们可以直接删除传入的props,这样if始终成立,始终return false,也就是该组件不会更新,效果如下

image.png

React.PureComponent

PureComponent的用法相对shouldComponentUpdate来说更加简单,首先注释掉我们刚才写的代码

接下来我们第一步要做的就是将GrandParent组件中引入的Component改为PureComponent,如下

import React, {PureComponent} from "react"
import Parent from "./Parent"

class GrandParent extends PureComponent {

所以这时候重新运行,就会发现GrandParent组件已经不会随着App的渲染而重新渲染了。

那么如何让它能够重新渲染呢,答案也很简单,就是传入一个会改变的props,所以在这里就可以传入 <GrandParent count={this.state.count}/>,这样就获得了与shouldComponentUpdate类似的功能

React.memo()

在了解了前面两种方法后,这种方法也就非常容易理解了。

React.memo()是React官方写的一个高阶组件,它与PureComponent非常相像,只是一个用于class组件,而React.memo用于function组件,并且不对state进行判断。

所以它的用法如下,

import React from "react"
import Parent from "./Parent"

function GrandParent(props) {
    console.log("[👴🏼]   [ ]   [ ]   [ ] rendered")
    return (
        <div>
            <p>I'm a GrandParent Component</p>
            <Parent />
            <Parent />
        </div>
    )
}

export default React.memo(GrandParent)

将组件化为函数组件,使用高阶组件进行加工后输出。此时点"+1"也不会再渲染GrandParent组件了

至于如何让GrandParent组件重新跟着渲染,那么答案也是一样的,在App.js中向GrandParent组件传入会随着改变的props即可

同理,如果是不想让Parent组件跟着渲染,只要对Parent.js进行上面三种方法的操作即可,以React.memo为例,代码如下

import React from "react"
import Child from "./Child"

function Parent(props){
    console.log("[ ]   [👩🏼‍⚕️]   [ ]   [ ] rendered")
    return (
        <div>
            <p>I'm a Parent Component</p>
            <Child />
            <Child />
        </div>
    )
}

export default React.memo(Parent)

效果如下

image.png

小结

shouldComponentUpdate()React.PureComponet以及React.memo()三种方法都能有效地防止组件被重复渲染,从而优化性能。在使用方面,shouldComponentUpdate的灵活性较高,但是大多数时候React.PureComponet已经可以满足使用需求。

所以class组件直接使用React.PureComponet,而function组件使用React.memo()即可。