React性能优化

React性能优化SCU

生命周期方法 shouldComponentUpdate(nextProps, nextState, nextContext)简称”SCU”

如果你定义了 shouldComponentUpdate,React 将调用它来确定是否可以跳过重新渲染。

如果你确定你想手动编写它,你可以将 this.propsnextProps 以及 this.statenextState 进行比较,并返回 false 来告诉 React 可以跳过更新。

import React, { Component } from 'react'
import { flushSync } from "react-dom"

function Son(props) {
  return <h2>我是子组件{props.count}</h2>
}

export class App extends Component {
  constructor() {
    super()
    this.state = { count: 0 }
  }
  shouldComponentUpdate(nextProps, nextState, nextContext) {
    // 进行操作比较确定是否重新渲染界面
    if (this.state.count !== nextState.count) {
      return true;
    }
    return false;
  }
  add() {
     this.setState({ count: this.state.count + 1 })
  }
  add1() {
    this.setState({ count: this.state.count - 1 })
 }
  render() {
    const { count } = this.state
    console.log("render函数被执行");
    return (
      <div>
        <h1>{"count值:" + count}</h1>
        <button onClick={e => this.add()}>修改count</button>
        <button onClick={e => this.add1()}>修改count1</button>
        <Son count={count} />
      </div>
    )
  }
}

export default App

当收到新的 props 或 state 时,React 会在渲染之前调用 shouldComponentUpdate,默认值为 true。初始渲染或使用 forceUpdate 时将不会调用此方法。

参数

  • nextProps:组件即将用来渲染的下一个 props。将 nextProps 与 this.props 进行比较以确定发生了什么变化。
  • nextState:组件即将渲染的下一个 state。将 nextState 与 this.state 进行比较以确定发生了什么变化。
  • nextContext:组件将要渲染的下一个 context。将 nextContext 与 this.context 进行比较以确定发生了什么变化。仅当你指定了 static contextType(更新的)或 static contextTypes(旧版)时才可用。

返回值 

如果你希望组件重新渲染的话就返回 true。这是也是默认执行的操作。

返回 false 来告诉 React 可以跳过重新渲染。

注意

  • 此方法 仅仅 作为性能优化而存在。如果你的组件在没有它的情况下损坏,请先修复组件。
  • 可以考虑使用 PureComponent 而不是手写 shouldComponentUpdatePureComponent 会浅比较 props 和 state 以及减少错过必要更新的概率。
  • 我们不建议在 shouldComponentUpdate 中使用深度相等检查或使用 JSON.stringify。因为它使性能变得不可预测并且它与每个 prop 和 state 的数据结构有关。在最好的情况下,你可能会面临给应用程序引入多秒停顿的风险,而在最坏的情况下,你可能会面临使应用程序崩溃的风险。
  • 返回 false 并不会阻止子组件在 他们的 state 发生变化时重新渲染。
  • 返回 false 并不能 确保 组件不会重新渲染。React 将使用返回值作为提示,但如果是出于其他有意义的原因,它仍然可能选择重新渲染你的组件。

注意

使用 shouldComponentUpdate 来优化类式组件与使用 memo 来优化函数式组件类似。函数式组件还使用 useMemo 来提供更精细的优化。

PureComponent★★★

PureComponentComponent 的子类,并且支持 所有 Component 的 API。继承 PureComponent 的子类相当与定义了一个自定义的 shouldComponentUpdate 方法,该方法将浅比较 props 和 state。

跳过类式组件不必要的重新渲染 

当父组件重新渲染时,React 通常会重新渲染子组件。为了优化性能,你可以创建一个组件,在父组件重新渲染时不会重新渲染,前提是新的 props 和 state 与旧的 props 和 state 相同。类式组件可以通过继承 PureComponent 来选择此行为

  • 如果你的组件正在使用的 context 发生变化,它仍会重新渲染。
import React, { PureComponent } from 'react'
//子组件
class Son extends PureComponent {
  render() {

    console.log("我是子组件观察时候执行更新");
    const { message } = this.props
    return (
      <>
        <h1>{message}</h1>
      </>
    )
  }
}

export class App extends PureComponent {
  constructor() {
    super()
    this.state = {
      count: 0,
      message: "林夕"
    }
  }
  add() {
    this.setState({ count: this.state.count + 1 })
  }
  changeText() {
    this.setState({ message: "修改后的林夕" })
  }
  render() {
    const { count, message } = this.state
    console.log("render函数被执行");
    return (
      <div>
        <h1>{"count值:" + count}</h1>
        <h1>{"message值:" + message}</h1>
        <button onClick={e => this.add()}>修改count</button>
        <button onClick={e => this.changeText()}>修改message</button>
        <Son message={message}></Son>
      </div>
    )
  }
}

export default App

但是在函数组件里面无论我们是否修改每次父组件渲染都会渲染一次子组件

我们可以,将其包装在 memo

import React, { PureComponent, memo } from 'react'
const Son = memo(function Son(props) {
  console.log("修改count,函数组件是否执行");
  return <h2>我是子组件{props.message}</h2>
})



export class App extends PureComponent {
  constructor() {
    super()
    this.state = {
      count: 0,
      message: "林夕"
    }
  }
  add() {
    this.setState({ count: this.state.count + 1 })
  }
  changeText() {
    this.setState({ message: "修改后的林夕" })
  }
  render() {
    const { count, message } = this.state
    console.log("render函数被执行");
    return (
      <div>
        <h1>{"count值:" + count}</h1>
        <h1>{"message值:" + message}</h1>
        <button onClick={e => this.add()}>修改count</button>
        <button onClick={e => this.changeText()}>修改message</button>
        <Son message={message}></Son>
      </div>
    )
  }
}

export default App

注意:PureComponent 不同,memo 不会比较新旧 state。在函数组件中,即使没有 memo,调用具有相同 state 的 set 函数默认已经阻止了重新渲染

不可变数据的力量★★★

避免更改你正用于 props 或 state 的值,如果要修改需要拿到构造函数中定义的原数据进行操作

如果需要修改深层数据建议使用如下方式

//...
constructor() {
    super()
    this.state = {
      arrayList: [
        { name: "林夕", age: 18, count: 1 },
        { name: "林夕1", age: 18, count: 2 },
        { name: "林夕2", age: 18, count: 3 },
      ]
    }
  }
addCount(index) {
    let arrayList = [...this.state.arrayList];
    arrayList[index].count++
    this.setState({arrayList})
 }
//...

ES6 数组支持扩展运算符

handleClick() {
  this.setState(state => ({
    words: [...state.words, 'marklar'],
  }));
};

你可以用类似的方式改写代码来避免可变对象的产生。例如,我们有一个叫做 colormap 的对象。我们希望写一个方法来将 colormap.right 设置为 'blue'。我们可以这么写:

function updateColorMap(colormap) {
  colormap.right = 'blue';
}

为了不改变原本的对象,我们可以使用 Object.assign 方法:

function updateColorMap(colormap) {
  return Object.assign({}, colormap, {right: 'blue'});
}

现在 updateColorMap 返回了一个新的对象,而不是修改老对象。Object.assign 是 ES6 的方法,需要 polyfill。

这里有一个 JavaScript 的提案,旨在添加对象扩展属性以使得更新不可变对象变得更方便:

function updateColorMap(colormap) {
  return {...colormap, right: 'blue'};
}

原文链接:https://juejin.cn/post/7326268968959852571 作者:林夕1120

(0)
上一篇 2024年1月22日 上午10:51
下一篇 2024年1月22日 上午11:02

相关推荐

发表回复

登录后才能评论