react性能优化

76 阅读4分钟

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

在react中存在shouldComponentUpdate用来根据state是否变化来判断是否要重新render组件,如果组件没有改变,那么就不会重新渲染组价,否则才会重新执行render,有了这样的一个生命周期函数,那么程序在运行的时候就可以减少一大部分不必要的性能开销。

预先知识

我们知道在js中主要有三种方式来进行判断是否相同

- 1. ==

- 2. ===

- 3. Object.is()

其中第一种在比较的时候会进行类型转换,不是严格意义上的相等。第二种在比较的时候会先进行类型的比较,只有类型相同的情况下,数值又相同才是真正的相等,但是在比较的时候存在几种特例:NaN不等于NaN,+0等于-O,而第三种和第二种的区别就是这两个特例其NaN等于NaN,+0不等于-O

shouldComponentUpdate介绍

先来浅看一个例子:

import React, { Component } from 'react';

class App extends Component {
  state={
    count:1
  }
  addOne=()=>{
    this.setState((state)=>({count:state.count}))
  }
  render() {
    return (
      <div>
        {this.state.count}
        <h1>{Math.random()}</h1>
        <button onClick={this.addOne}>点我加一</button>
      </div>
    );
  }
}

export default App;

如果组件重新渲染那么我们的随机数肯定就会改变,我们每次都设置我们的state为原来的值,这样如果还进行重新的渲染那么很明显是不合理的,但是实际运行起来它就是会进行重新渲染:

GIF 2022-5-26 11-16-44.gif

这时候我们的shouldComponentUpdate就要起作用了。

  shouldComponentUpdate(nextProps, nextState){
    if(根据实际的需求){
        return true;
    }else{
        return false;
    }
  }

它接受两个参数,一个是下一个props,一个是下一个state,在组件中我们可以很方便的获取当前的state和props,所以只要我们对当前值和下一个值进行逻辑判断,再来决定是否需要更新组件就可以节省很多的性能。如这个例子我们只要这么写就可以了:

import React, { Component } from 'react';

class App extends Component {
  state={
    count:1
  }
  shouldComponentUpdate(nextProps, nextState){
    return nextState.count!==this.state.count
  }
  addOne=()=>{
    this.setState((state)=>({count:state.count}))
  }
  addTrueOne=()=>{
    this.setState((state)=>({count:state.count+1}))
  }
  render() {
    return (
      <div>
        {this.state.count}
        <h1>{Math.random()}</h1>
        <button onClick={this.addOne}>点我加一</button>
        <button onClick={this.addTrueOne}>点我真的加一</button>
      </div>
    );
  }
}

export default App;

这样我们就能在state真的变化的时候才进行组件的更新,避免了很多不必要的更新:

2.gif

很多时候我们的状态比较并不那么复杂,如果每次都要自己写shouldComponentUpdate又有点麻烦了,而且一旦写了这个生命周期函数就必须要返回 true 或者 false,如果写错了,那么可能反而适得其反。于是官方提出了一个新的继承类PureComponent,也就是说,我们在写类组件的时候,不在直接继承React.Component而是继承React.PureComponent,它内部就会帮我们实现shouldComponetUpdate,而这个比较是浅比较。

浅比较

比较规则和Object.is()类似

- 通过is函数对两个参数进行比较,判断是否相同,相同直接返回true:基本数据类型值相同,同一个引用对象都表示相同

- 如果两个参数不相同,判断两个参数是否至少有一个不是引用类型,存在即返回false,如果两个都是引用类型对象,则继续下面的比较;

- 判断两个不同引用类型对象是否相同。先通过Object.keys获取到两个对象的所有属性,具有相同属性,且每个属性值相同即两个对相同(相同也通过is函数完成)。

这里所谓的是指在比较的时候不会对属性是引用类型的继续进行比较(一旦属性是引用类型,如果地址值不同就直接不相等了),但是这其实是不合理,因为内容一样的更新在我们看来是不合理的。那么为啥react官方要采用浅比较呢?对于引用类型的比较如果进行递归比较内容好像也不难实现啊?这其实是出于性能的考虑。如果在更新判断中不断的执行递归操作去比较先后的state和props,毫无以为是要产生很大的开销的,所以干脆直接默认浅比较,如果你明确知道这里可能会出现性能问题,那你应该自己重新实现shouldComponentUpdate!

总结

在组件更新前,我们应该判断一下这样的更新是否是有意义的。