美文网首页
react 常见性能优化

react 常见性能优化

作者: 暴躁程序员 | 来源:发表于2023-10-08 09:38 被阅读0次

    一、避免不必要的 render 执行

    当父组件的任意状态(state或props)发生变更时,父组件会重新渲染(执行render),同时依赖父组件的所有子组件也都会重新渲染(执行render),但是我们希望只有当父组件传递给子组件的 props 发生变更时,子组件才会重新渲染

    1. 解决方式一:使用 shouldComponentUpdate 优化(只适用于类组件)

    需要 immutable 脚本库配合来判断 state 和 props 是否变化,变化执行render,不变化不执行 render

    1. 在父组件中使用 shouldComponentUpdate
    import React, { Component } from "react";
    import OptimizeChildView from "./OptimizeChildView"
    import { Map,is } from 'immutable'; // 可用来判断对象的值是否相等
    
    class Test extends Component {
      constructor(props) {
        super(props);
        this.state = {
          height: 170,
          age: 20,
        };
        this.changeAge = this.changeAge.bind(this) /* 修正this指向 */
        this.changeHeight = this.changeHeight.bind(this) /* 修正this指向 */
        this.changeHeightNo = this.changeHeightNo.bind(this) /* 修正this指向 */
      }
      render() {
        console.log('父组件 render');
        return (
          <>
            <h1>父组件</h1>
            <div>{this.state.age}</div>
            <div>{this.state.height}</div>
            <button onClick={this.changeAge}>改变当前组件的state、改变传入子组件的props</button>
            <button onClick={this.changeHeight}>改变当前组件的state、不改变传入子组件的props</button>
            <button onClick={this.changeHeightNo}>不改变当前组件的state、不改变传入子组件的props</button>
            <hr />
            <OptimizeChildView age={this.state.age} />
          </>
        );
      }
      // 结果:父子组件 render 都会执行(√)
      changeAge(e) {
        this.setState((prevState) => {
          return { age: prevState.age + 1 };
        });
      }
      // 结果:父组件render会执行,子组件render不会执行(√)
      changeHeight(e) {
        this.setState((prevState) => {
          return { height: prevState.height + 1 };
        });
      }
      // 结果:父子组件 render 都不会执行(√)
      changeHeightNo(e) {
        this.setState((prevState) => {
          return { height: prevState.height };
        });
      }
      // shouldComponentUpdate 优化
      shouldComponentUpdate(nextProps,nextState){
        const that = this
        if(is(Map(nextProps),Map(that.props)) && is(Map(nextState),Map(that.state))){
          return false // 代表父组件传过来的props和当前组件的state都没变化,不执行 render
        }
        return true
      }
    }
    export default Test;
    
    1. 在子组件中使用 shouldComponentUpdate
    import React, { Component } from "react";
    import { Map,is } from 'immutable'; // 可用来判断对象的值是否相等
    class Test extends Component {
      constructor(props) {
        super(props);
        this.state = {};
      }
      render() {
        console.log('子组件 render');
        return (
          <> 
            <h1>子组件</h1>
            <div>{this.props.age}</div>
          </>
        );
      }
      // 代表父组件传过来的props和当前组件的state都没变化,不执行 render
      shouldComponentUpdate(nextProps,nextState){
        const that = this
        if(is(Map(nextProps),Map(that.props)) && is(Map(nextState),Map(that.state))){
          return false 
        }
        return true
      }
    }
    export default Test;
    

    2. 解决方式二:使用 PureComponent 优化 (只适用于类组件)

    React.PureComponent 组件采用对属性和状态用浅比较的方式在组件内部实现了 shouldComponentUpdate()

    1. 在父组件中使用 PureComponent
    import React, { PureComponent } from "react";
    import OptimizeChildView from "./OptimizeChildView"
    
    class Test extends PureComponent {
      constructor(props) {
        super(props);
        this.state = {
          height: 170,
          age: 20,
        };
        this.changeAge = this.changeAge.bind(this) /* 修正this指向 */
        this.changeHeight = this.changeHeight.bind(this) /* 修正this指向 */
        this.changeHeightNo = this.changeHeightNo.bind(this) /* 修正this指向 */
      }
      render() {
        console.log('父组件 render');
        return (
          <>
            <h1>父组件</h1>
            <div>{this.state.age}</div>
            <div>{this.state.height}</div>
            <button onClick={this.changeAge}>改变当前组件的state、改变传入子组件的props</button>
            <button onClick={this.changeHeight}>改变当前组件的state、不改变传入子组件的props</button>
            <button onClick={this.changeHeightNo}>不改变当前组件的state、不改变传入子组件的props</button>
            <hr />
            <OptimizeChildView age={this.state.age} />
          </>
        );
      }
      // 结果:父子组件 render 都会执行(√)
      changeAge(e) {
        this.setState((prevState) => {
          return { age: prevState.age + 1 };
        });
      }
      // 结果:父组件render会执行,子组件render不会执行(√)
      changeHeight(e) {
        this.setState((prevState) => {
          return { height: prevState.height + 1 };
        });
      }
      // 结果:父子组件 render 都不会执行(√)
      changeHeightNo(e) {
        this.setState((prevState) => {
          return { height: prevState.height };
        });
      }
    }
    export default Test;
    
    1. 在子组件中使用 PureComponent
    import React, { PureComponent } from "react";
    class Test extends PureComponent {
      constructor(props) {
        super(props);
        this.state = {};
      }
      render() {
        console.log('子组件 render');
        return (
          <> 
            <h1>子组件</h1>
            <div>{this.props.age}</div>
          </>
        );
      }
    }
    export default Test;
    

    3. 解决方式三:使用 React.memo 优化 (只适用于无状态组件)

    React.memo 为高阶组件,需要传入两个参数,第一个参数是被缓存的组件,第二个参数是回调函数,只有当第二个回调函数返回true时才会重新渲染缓存组件
    一般不传第二个参数(不传:只有当子组件依赖父组件的props发生变化才重新渲染子组件,传:状态变化满足某个条件才重新渲染子组件)

    1. 在父组件中
    import { useState } from "react";
    import OptimizeChildView from "./OptimizeChildView";
    function OptimizeView() {
      const [height, setHeight] = useState(170);
      const [age, setAge] = useState(20);
    
      // 结果:父子组件 render 都会执行(√)
      function changeAge(e) {
        setAge(age + 1);
      }
      // 结果:父组件render会执行,子组件render不会执行(√)
      function changeHeight(e) {
        setHeight(height + 1);
      }
      // 结果:父子组件 render 都不会执行(√)
      function changeHeightNo(e) {
        setHeight(height);
      }
      console.log('父组件 render');
      return (
        <>
          <h1>父组件</h1>
          <div>{age}</div>
          <div>{height}</div>
          <button onClick={changeAge}>
            改变当前组件的state、改变传入子组件的props
          </button>
          <button onClick={changeHeight}>
            改变当前组件的state、不改变传入子组件的props
          </button>
          <button onClick={changeHeightNo}>
            不改变当前组件的state、不改变传入子组件的props
          </button>
          <hr />
          <OptimizeChildView age={age} />
        </>
      );
    }
    
    export default OptimizeView;
    
    1. 在子组件中使用 memo 高阶函数包裹组件
    import { memo } from "react";
    function OptimizeChildView(props) {
      console.log('子组件 render');
      return (
        <>
          <h1>子组件</h1>
          <div>{props.age}</div>
        </>
      );
    }
    
    export default memo(OptimizeChildView);
    

    二、避免在 render 函数中使用内联函数和在 render 函数中使用bind绑定this

    否则每次执行 render 函数都会创建新的函数实例

    1. 解决方式:使用非内联的方式定义点击函数,在 constructor 中使用bind绑定this
    import React, { Component } from "react";
    import OptimizeChildView from "./OptimizeChildView";
    class Test extends Component {
      constructor(props) {
        super(props);
        this.state = {
          name: "zhangsan",
          age: 20,
        };
        this.changeAge = this.changeAge.bind(this) /* 修正this指向 */
      }
    
      render() {
        return (
          <>
            <div>{this.state.name}</div>
            <div>{this.state.age}</div>
            <button onClick={this.changeAge}>函数的正确打开方式</button>
            <button onClick={(e) => {this.setState((prevState) => {return { age: prevState.age + 1 }})}}>内联函数</button>
            <OptimizeChildView />
          </>
        );
      }
      changeAge(e) {
        this.setState((prevState) => {
          return { age: prevState.age + 1 };
        });
      }
    }
    export default Test;
    

    三、避免创建不必要的组件最外层元素标签

    1. 解决方式:使用 <></> 代替 <div></div>充当组件最外层元素标签
    import React, { Component } from "react";
    
    class Test extends Component {
      /* render() {
        return (
          <div> 
            <div>hello world</div>
            <div>hello world</div>
          </div>
        );
      } */
    
      // 避免生成多余的外层 div
      render() {
        return (
          <> 
            <div>hello world</div>
            <div>hello world</div>
          </>
        );
      }
    }
    export default Test;
    

    相关文章

      网友评论

          本文标题:react 常见性能优化

          本文链接:https://www.haomeiwen.com/subject/rvfbbdtx.html