如果你对 React Component 一知半解,那么请看这篇文章!

一、笔者述 :

笔者最近在查阅关于 React 生命周期的相关资料来构建自己的 React 组件的生命体系的时候发现同行们在记录自己对 React 组件及生命周期的理解时,或多或少都提到了有关 React 组件优化 方面的一些思路。这对笔者的帮助实在是不可小觑,在 SPA 模式大行其道的年代,对代码编译的速度与效率的确是一场考验,下面是笔者对 React 组件优化方面的一些探索 :

二、React 组件优化的两种思路 :

1. 使用 React.Component 来构建 React 组件,通过 shouldComponentUpdate 钩子来进行优化

-1). 优化前 :

导语 : 其实 React.Component 是咱们经常使用的,但是大多数情况下。咱们都是草草的完成业务逻辑,可能没有进一步想过 : "当一个父组件有两个 state 属性 【A、B】,A 渲染到当前父组件上,B 注入到子组件中去显示。父组件上有一个 btn ,当点击 btn 的时候在其回调中更新 state 中的 A 属性 : 试想一下此时咱们没有重新向子组件注入新的 B 属性值【就是说 state 中的 B 属性始终没变化】,那么子组件会重新 render 吗 ? "
答案 : 子组件会重新 render
结论 : 无论父组件向子组件重新传入的值是否发生变化子组件都会重新 render
图解 :
在这里插入图片描述
子组件明明没有变化却莫名的被重新 render 了一次,这种结果是咱们不愿意看到的,也是 React 组件可以优化的地方。
代码 :
父组件 :

// 导入 React 官方包
import React from 'react';

// 导入自定义包
import ShowAge from './components/ShowAge';

// 测试 React Component ...
class App extends React.Component {

  // 初始化数据
  constructor(props) {
    super(props);
    console.log("react component parent constructor ...");

    this.state = {
      name: "FruitJ",
      age: 22
    };
  }

  static getDerivedStateFromProps(props, state) {
    console.log("react component parent getDerivedStateFromProps ...");
    return null;
  }

  componentDidMount() {
    console.log("react component parent componentDidMount ...");
  }

  // btn's click callback
  handleClick = () => {
    console.log("react component parent click ...");
    // update state
    this.setState({
      name: "LJ",
    });
  };

  render() {

    const { name, age } = this.state;

    return (
        <div className="App">
          <h1>Hello React Component, My name is { name }</h1>
          <ShowAge age = { age } />
          <button onClick = { this.handleClick }>
            parent 点我啊 ~
          </button>
        </div>
    );
  }

  shouldComponentUpdate(nextProps, nextState, nextContext) {
    console.log("react component parent shouldComponentUpdate ...");
    return true;
  }

  getSnapshotBeforeUpdate(prevProps, prevState) {
    console.log("react component parent getSnapshotBeforeUpdate ...");
    return "";
  }

  componentDidUpdate(prevProps, prevState, snapshot) {
    console.log("react component parent componentDidUpdate ...");
  }

  componentWillUnmount() {
    console.log("react component parent componentWillUnmount ...");
  }
}
export default App;

子组件 :

import React, { Component } from 'react'

class ShowAge extends Component {// 定义组件

    // 初始化数据
    constructor(props) {
        super(props);
        console.log("react component child constructor ...");
        this.state = {
            hobby: "coding"
        };
    }

    static getDerivedStateFromProps(props, state) {

        console.log("react component child getDerivedStateFromProps ...");
        return null;
    }

    componentDidMount() {
        console.log("react component child componentDidMount ...");
    }

    handleClick = () => {
        console.log("react component child handleClick ...");
        // update state
        this.setState({
            hobby: "read source code!"
        });
    };

    render() {
        return (
            <div>
                <h3>props age : { this.props.age }</h3>
                <h3>state hobby : { this.state.hobby }</h3>
                <button onClick = { this.handleClick }>
                    child 点我啊 ~
                </button>
            </div>
        );
    }

    shouldComponentUpdate(nextProps, nextState, nextContext) {
        console.log("react component child shouldComponentUpdate ...");
    }

    getSnapshotBeforeUpdate(prevProps, prevState) {
        console.log("react component child getSnapshotBeforeUpdate ...");
        return "";
    }

    componentDidUpdate(prevProps, prevState, snapshot) {
        console.log("react component child componentDidUpdate ...");
    }

    componentWillUnmount() {
        console.log("react component child componentWillUnmount ...");
    }
}
export default ShowAge;

动图 :
在这里插入图片描述

-2). 优化后 :

方案一 : 浅比较

导语 : 浅比较的特点 : 仅仅属于表层比较不会触及更深层次的比较 ( 对于基本类型仅比较基本类型值,对于引用类型的比较则永远返回 false【具体原因下面会说到】 ),并且有一点需要言明 : " 笔者认为有些说法言道在 shouldComponentUpdate 周期方法中进行引用类型的比较云云 ... 这里笔者 掷地有声 回答,那种说法是错的 ! 因为 nextProps 与 this.props 、nextState 与 this.state这两对儿的对比者都是不同的两个对象,他们的引用怎么可能相同 ? 别不用说其下挂载的数据了,除了基本类型值其余的都是没法比较的! "
图解 :
在这里插入图片描述
代码 :
父组件 :

// 导入 React 官方包
import React from 'react';

// 导入自定义包
import ShowAge from './components/ShowAge';

// 测试 React Component ...
class App extends React.Component {

  // 初始化数据
  constructor(props) {
    super(props);
    console.log("react component parent constructor ...");

    this.state = {
      name: "FruitJ",
      age: 22
    };
  }


  static getDerivedStateFromProps(props, state) {
    console.log("react component parent getDerivedStateFromProps ...");
    return null;
  }


  componentDidMount() {
    console.log("react component parent componentDidMount ...");
  }

  // btn's click callback
  handleClick = () => {
    console.log("react component parent click ...");
    // update state
    this.setState({
      name: "LJ",
    });
  };

  handleChange = (val) => {

    this.setState({
      age: val
    });
  };

  render() {
    console.log("react component parent render ...");
    const { name, age } = this.state;

    return (
        <div className="App">
          <h1>Hello React Component, My name is { name }</h1>
          <ShowAge age = { this.state.age }
                   handleChange = { this.handleChange }
          />
          <button onClick = { this.handleClick }>
            parent 点我啊 ~
          </button>
        </div>
    );
  }

  shouldComponentUpdate(nextProps, nextState, nextContext) {
    console.log("react component parent shouldComponentUpdate ...");
    return true;
  }

  getSnapshotBeforeUpdate(prevProps, prevState) {
    console.log("react component parent getSnapshotBeforeUpdate ...");
    return "";
  }

  componentDidUpdate(prevProps, prevState, snapshot) {
    console.log("react component parent componentDidUpdate ...");
  }

  componentWillUnmount() {
    console.log("react component parent componentWillUnmount ...");
  }
}
export default App;

子组件 :

import React, { Component } from 'react'

class ShowAge extends Component {// 定义组件

    // 初始化数据
    constructor(props) {
        super(props);
        console.log("react component child constructor ...");
        this.state = {
            hobby: "coding"
        };
    }

    static getDerivedStateFromProps(props, state) {
        console.log("react component child getDerivedStateFromProps ...");
        return null;
    }

    componentDidMount() {
        console.log("react component child componentDidMount ...");
    }

    handleClick = () => {
        console.log("react component child handleClick ...");
        // update state
        this.setState({
            hobby: "read source code!"
        });
    };

    render() {

        console.log("react component child render ...");

        return (
            <div>
                <h3>props age : { this.props.age }</h3>
                <h3>state hobby : { this                  .state.hobby }</h3>
                <button onClick = { () => {this.props.handleChange(23)} }>
                    child 点我啊 ~
                </button>
            </div>
        );
    }

    shouldComponentUpdate(nextProps, nextState, nextContext) {
        console.log("react component child shouldComponentUpdate ...");
		// 优化 - 浅比较
        return !(nextProps.age === this.props.age);
    }

    getSnapshotBeforeUpdate(prevProps, prevState) {
        console.log("react component child getSnapshotBeforeUpdate ...");
        return "";
    }

    componentDidUpdate(prevProps, prevState, snapshot) {
        console.log("react component child componentDidUpdate ...");
    }

    componentWillUnmount() {
        console.log("react component child componentWillUnmount ...");
    }
}
export default ShowAge;

动图 :
在这里插入图片描述

方案二 : 深比较

导语 : 深比较则是针对引用数据类型而言的,我们可以进行深比较,上面我们说过直接对比引用类型地址是不可能也不合适的,不能是因为两对儿对比对象 (nextStatethis.statenextPropsthis.props) 的地址一定不同,所以用返回 false ,也就失去了意义。其次即使真的可以这样比,意义也不大,因为仅仅是比较地址而不比较其内部的属性变化,咱们是无法及时且正确的判定组件是否应该更新还是停止渲染 …。
所以笔者认为, React.Component 中 shouldComponentUpdate 中通过深比较来优化 react 组件的方式,首先是针对引用类型,其次是迭代引用类型的所有属性,并判断对比双方的所有属性值是否相同,有一个不同的情况都要重新渲染组件否则则停止渲染。
图解 :
在这里插入图片描述
代码 :

// 导入 React 官方包
import React from 'react';

// 导入自定义包
import ShowAge from './components/ShowAge';

// 测试 React Component ...
class App extends React.Component {

  // 初始化数据
  constructor(props) {
    super(props);
    console.log("react component parent constructor ...");

    this.state = {
      name: "FruitJ",
      age: 22
    };
  }


  static getDerivedStateFromProps(props, state) {
    console.log("react component parent getDerivedStateFromProps ...");
    return null;
  }


  componentDidMount() {
    console.log("react component parent componentDidMount ...");
  }

  // btn's click callback
  handleClick = () => {
    console.log("react component parent click ...");
    // update state
    this.setState({
      name: "LJ",
    });
  };

  handleChange = (val) => {

    this.setState({
      age: val
    });
  };

  render() {
    console.log("react component parent render ...");
    const { name, age } = this.state;

    return (
        <div className="App">
          <h1>Hello React Component, My name is { name }</h1>
          <ShowAge age = { this.state.age }
                   handleChange = { this.handleChange }
          />
          <button onClick = { this.handleClick }>
            parent 点我啊 ~
          </button>
        </div>
    );
  }

  shouldComponentUpdate(nextProps, nextState, nextContext) {
    console.log("react component parent shouldComponentUpdate ...");
    return true;
  }

  getSnapshotBeforeUpdate(prevProps, prevState) {
    console.log("react component parent getSnapshotBeforeUpdate ...");
    return "";
  }

  componentDidUpdate(prevProps, prevState, snapshot) {
    console.log("react component parent componentDidUpdate ...");
  }

  componentWillUnmount() {
    console.log("react component parent componentWillUnmount ...");
  }
}
// 测试 React PrueComponent

export default App;

子组件 :

import React, { Component } from 'react'

class ShowAge extends Component {// 定义组件

    // 初始化数据
    constructor(props) {
        super(props);
        console.log("react component child constructor ...");
        this.state = {
            name: "XXY",
            hobby: "coding"
        };
    }

    static getDerivedStateFromProps(props, state) {
        console.log("react component child getDerivedStateFromProps ...");
        return null;
    }

    componentDidMount() {
        console.log("react component child componentDidMount ...");
    }

    handleClick = () => {
        console.log("react component child handleClick ...");
        // update state
        this.setState({
            // hobby: "read source code!"
            hobby: "coding"
        });
    };

    render() {

        console.log("react component child render ...");

        return (
            <div>
                <h3>props age : { this.props.age }</h3>
                <h3>state hobby : { this                  .state.hobby }</h3>
                <button onClick = { this.handleClick }>
                    child 点我啊 ~
                </button>
            </div>
        );
    }

    shouldComponentUpdate(nextProps, nextState, nextContext) {
        console.log("react component child shouldComponentUpdate ...");
        // 检测当前组件的 state 对象中的每个属性值 ( 深比较 )
        let arr = Object.keys(nextState);
        try {
            return !arr.every((item, index) => nextState[arr[index]] === this.state[arr[index]]);
        }catch(err) {
            return true;
        }
    }

    getSnapshotBeforeUpdate(prevProps, prevState) {
        console.log("react component child getSnapshotBeforeUpdate ...");
        return "";
    }

    componentDidUpdate(prevProps, prevState, snapshot) {
        console.log("react component child componentDidUpdate ...");
    }

    componentWillUnmount() {
        console.log("react component child componentWillUnmount ...");
    }
}
export default ShowAge;

动图 :
在这里插入图片描述

2. 使用 React.PrueComponent 来构建 React 组件,默认自带优化效果

导语 : 我们可以使用 React.PureComponent 来定义 React 组件,因为其自带优化效果,并且如果应用确实需要优化,该方式也是 React 官方推荐使用的,而不是自己使用 React.Component"定制" ,但是 React.PureComponent 也有不足之处 : 就是简单的数据结构可以进行优化,但是复杂的数据结构 React.Component 也无能为力。
代码 :
父组件 :

// 导入 React 官方包
import React from 'react';

// 导入自定义包
import ShowAge from './components/ShowAge';

// 测试 React Component ...
class App extends React.PureComponent {

  // 初始化数据
  constructor(props) {
    super(props);
    console.log("react component parent constructor ...");

    this.state = {
      name: "FruitJ",
      age: 22
    };
  }


  static getDerivedStateFromProps(props, state) {
    console.log("react component parent getDerivedStateFromProps ...");
    return null;
  }


  componentDidMount() {
    console.log("react component parent componentDidMount ...");
  }

  // btn's click callback
  handleClick = () => {
    console.log("react component parent click ...");
    // update state
    this.setState({
      name: "LJ",
    });
  };

  handleChange = (val) => {

    this.setState({
      age: val
    });
  };

  render() {
    console.log("react component parent render ...");
    const { name, age } = this.state;

    return (
        <div className="App">
          <h1>Hello React Component, My name is { name }</h1>
          <ShowAge age = { this.state.age }
                   handleChange = { this.handleChange }
          />
          <button onClick = { this.handleClick }>
            parent 点我啊 ~
          </button>
        </div>
    );
  }

  getSnapshotBeforeUpdate(prevProps, prevState) {
    console.log("react component parent getSnapshotBeforeUpdate ...");
    return "";
  }

  componentDidUpdate(prevProps, prevState, snapshot) {
    console.log("react component parent componentDidUpdate ...");
  }

  componentWillUnmount() {
    console.log("react component parent componentWillUnmount ...");
  }
}
// 测试 React PrueComponent

export default App;

子组件 :

import React, { Component } from 'react'

class ShowAge extends React.PureComponent {// 定义组件

    // 初始化数据
    constructor(props) {
        super(props);
        console.log("react component child constructor ...");
        this.state = {
            name: "XXY",
            hobby: "coding",
            arr: [1, 2, 3, 4, 5, 6]
        };
    }

    static getDerivedStateFromProps(props, state) {
        console.log("react component child getDerivedStateFromProps ...");
        return null;
    }

    componentDidMount() {
        console.log("react component child componentDidMount ...");
    }

    handleClick = () => {
        console.log("react component child handleClick ...");
        // update state
        this.setState({
            // hobby: "read source code!",
            hobby: "coding",
            // arr: [1, 2, 3, 4, 5, 6]
        });
    };

    render() {

        console.log("react component child render ...");

        return (
            <div>
                <h3>props age : { this.props.age }</h3>
                <h3>state hobby : { this                  .state.hobby }</h3>
                {/*<button onClick = { () => {this.props.handleChange(23)} }>*/}
                <button onClick = { this.handleClick }>
                    child 点我啊 ~
                </button>
            </div>
        );
    }

    getSnapshotBeforeUpdate(prevProps, prevState) {
        console.log("react component child getSnapshotBeforeUpdate ...");
        return "";
    }

    componentDidUpdate(prevProps, prevState, snapshot) {
        console.log("react component child componentDidUpdate ...");
    }

    componentWillUnmount() {
        console.log("react component child componentWillUnmount ...");
    }
}
export default ShowAge;

动图 :
在这里插入图片描述

三、总述 :

如果是使用 React.Component 来定义的组件,就通过 shouldComponentUpdate 周期方法来优化组件,但须注意对于 基本类型 可以采取 浅比较的方式;对于 引用类型 可以采取 深比较 的方式。
如果是使用 React.PureComponent 来定义组件,默认其有着优化组件的策略,对于简单一点的数据结构可以达成我们的需求,但是复杂的数据结构 React.PureComponent 还是无能为力。
只有实在没办法的时候才可以通过 shouldComponentUpdate 进行定制化的深比较来实现需求。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值