美文网首页React面试
记录一些 PureComponent 的用法

记录一些 PureComponent 的用法

作者: 莫帆海氵 | 来源:发表于2020-09-13 16:59 被阅读0次

    React.PureComponent

    对它的第一印象依然停留在它是一个纯组件、渲染的次数更少、使用它对性能提升有好处等等。但对于它具体细节、原理不太清楚,一直对这个用法很好奇,想知道它到底差异在哪里?有什么不一样的地方?最近空闲下来把一些想法验证下

    1. 它和 Component 组件的区别?
    2. 是否可以维护自己的 state?用 state 后有什么区别?
    3. 哪些值的改变会触发 render?
    4. 它和 Component 组件使用 shouldComponentUpdate 后效果有什么区别?

    本文主要从比较 render 的次数差异上来比较

    // 打印 render 计数的方法
    function logRenderCount() {
      let that = this;
      if (!this.renderCount) this.renderCount = {};
    
      return function(inst) {
        let name = inst.__proto__.constructor.name;
        if (typeof that.renderCount[name] === "undefined") {
          that.renderCount[name] = 0;
        }
        let count = that.renderCount[name]++;
        console.log(`${name} render count = ${count}`);
        return inst;
      };
    }
    
    global.logRenderCount = new logRenderCount();
    

    它和 Component 组件的区别

    首先和最简单的 Component 组件做对比,传入一个基本的 props 属性,并通过方法改变父组件的值比较结果。

    从输出结果可以看出,如果只有简单的 props 属性,他们的作用是一样的,每次点击如果更改 props ,对应更新一次 render

    import React from "react";
    import Counter from "./counter";
    import CounterPure from "./counter_pure"
    
    class App extends React.Component {
      constructor(props) {
        super(props);
    
        this.state = {
          currentNavIndex: 0
        };
      }
    
      changeTabBar = index => {
        this.setState({
          currentNavIndex: index
        });
      }
    
      render() {
        const { currentNavIndex } = this.state
    
        return (
          <div>
            <h1>基本用法</h1>
            <Counter
              currentNavIndex={currentNavIndex}
              changeTabBar={this.changeTabBar}
            />
            <CounterPure
              currentNavIndex={currentNavIndex}
              changeTabBar={this.changeTabBar}
            />
          </div>
        );
      }
    }
    
    export default App;
    
    // Component 例子
    import React from "react"
    
    class Counter extends React.Component {
      changeTabBar = index => {
        console.log('Demo click')
        this.props.changeTabBar(index)
      }
    
      render() {
        global.logRenderCount(this)
        const { currentNavIndex } = this.props
    
        return (
          <div>
            <h3>Component</h3>
            {currentNavIndex}<br/>
            <button onClick={this.changeTabBar.bind(this, currentNavIndex + 1)}> click me </button>
          </div>
        )
      }
    }
    
    export default Counter
    
    // PureComponent 例子
    import React from "react"
    
    class CounterPure extends React.PureComponent {
      changeTabBar = index => {
        console.log('Demo click')
        this.props.changeTabBar(index)
      }
    
      render() {
        global.logRenderCount(this)
        const { currentNavIndex } = this.props
    
        return (
          <div>
            <h3>PureComponent</h3>
            {currentNavIndex}<br/>
            <button onClick={this.changeTabBar.bind(this, currentNavIndex + 1)}> click me </button>
          </div>
        )
      }
    }
    
    export default CounterPure
    
    // 输出结果如下
    // Demo click
    // Counter render count = 5
    // CounterPure render count = 2
    

    考虑父组件 changeTabBar 引入其它一些有副作用的 state 改变,结果会怎样呢?

    从输出结果可以看出,父组件 state 的变化都会引起 Component 组件的 render,但对于 PureComponent 组件只有关联的 props 才会影响 render

    changeTabBar = index => {
        this.setState(
          {
            currentNavIndex: index,
          }
        )
    
        setTimeout(() => {
          this.setState({
            obj: {
              m: Math.random(),
            },
          })
        }, 500)
      }
      
      // 输出如下
      // Demo click
      // Counter render count = 5
      // CounterPure render count = 2
      // Counter render count = 6
      
      // PureComponent 输出如下
      // Demo click
      // Demo render count = 2
      
      // Component 输出如下
      // Demo click
      // Demo render count = 18
      // Demo render count = 19
    

    但如果引入一些更多、更复杂的 props 属性结果会怎样呢?

    从结果看不管父组件是否有副作用的 state 改变,结果都是相同的,PureComponent Component 没有差异

    changeTabBar = index => {
        this.setState(
          {
            currentNavIndex: index,
            obj: {
              m: Math.random(),
            },
          }
        )
    
        setTimeout(() => {
          this.setState({
            obj: {
              m: Math.random(),
            },
          })
        }, 500)
      }
      
    import React from "react"
    
    class CounterPureComplex extends React.PureComponent {
      changeTabBar = index => {
        console.log('Demo click')
        this.props.changeTabBar(index)
      }
    
      render() {
        global.logRenderCount(this)
        const { currentNavIndex } = this.props
    
        return (
          <section>
            <h3>PureComponent</h3>
            {currentNavIndex}<br/>
            <pre>{JSON.stringify(this.props.obj)}</pre>
            <button onClick={this.changeTabBar.bind(this, currentNavIndex + 1)}> click me </button>
          </section>
        )
      }
    }
    
    export default CounterPureComplex
    
    
    // 输出结果如下
    // Demo click
    // Counter render count = 11
    // CounterPureComplex render count = 5
    // Counter render count = 12
    // CounterPureComplex render count = 6
    

    但如果父组件有副作用 state 的更改,只改变其中属性会怎样?

    从结果看 PureComponent 不会再次 render ,Component 会再次 render,所以 PureComponent 只实现了浅比较

    changeTabBar = index => {
        this.setState(
          {
            currentNavIndex: index,
            obj: {
              m: Math.random(),
            },
          }
        )
    
        setTimeout(() => {
          let obj = this.state.obj
          obj.m = Math.random()
    
          this.setState({
            obj: obj,
          })
        }, 500)
      }
      
      // 输出结果如下
      // Demo click
      // Counter render count = 13
      // CounterPureQuote render count = 1
      // Counter render count = 14
    

    是否可以维护自己的 state?用 state 后有什么区别?

    可以看出 PureComponent 可以使用 state, 用法和 Component 一样,使用 state 后 render 都受到父组件 props 和本身的 state 影响

    import React from "react"
    
    class CounterPureState extends React.PureComponent {
      constructor(props) {
        super(props)
    
        this.state = {
          currentNavIndex: props.currentNavIndex
        }
      }
    
      changeTabBar = index => {
        console.log('Demo click')
        this.setState({
          currentNavIndex: index,
        },() => {
          this.props.changeTabBar(index)
        })
        // 注意这里如果放在 setState 同级,输出结果就只有一次 render 了
        // this.props.changeTabBar(index)
      }
    
      render() {
        global.logRenderCount(this)
        const { currentNavIndex } = this.state
    
        return (
          <div>
            <h3>PureComponent</h3>
            {currentNavIndex}<br/>
            <button onClick={this.changeTabBar.bind(this, currentNavIndex + 1)}> click me </button>
          </div>
        )
      }
    }
    
    export default CounterPureState
    
    
    // 输出结果如下
    // Demo click
    // CounterPureState render count = 1
    // Counter render count = 15
    // CounterPureState render count = 2
    

    哪些值的改变会触发 render

    参照上例,可以看出 PureComponent 也受到组件自身的 state 和父组件的 props 影响,但不会受到父组件不相关的 state 影响而影响

    它和 Component 组件使用 shouldComponentUpdate 后效果有什么区别?

    从结果输出上看 Component 使用 shouldComponentUpdate 后和使用 PureComponent 的作用是一样的,但这依赖于 shouldComponentUpdate 具体如何实现

    import React from "react"
    
    class Demo extends React.Component {
      static defaultProps = {
        currentNavIndex: 0,
        changeTabBar: () => {},
      }
    
      constructor(props) {
        super(props)
    
        this.state = {
          currentNavIndex: props.currentNavIndex
        }
      }
    
      shouldComponentUpdate(nextProps, nextState) {
        if (nextProps.currentNavIndex !== this.props.currentNavIndex) {
          return true
        }
    
        if (nextState.currentNavIndex !== this.state.currentNavIndex) {
          return true
        }
    
        return false
      }
    
      changeTabBar = index => {
        console.log('Demo click')
        this.setState({
          currentNavIndex: index,
        },() => {
          this.props.changeTabBar(index)
        })
      }
    
      render() {
        global.logRenderCount(this)
        const { currentNavIndex } = this.state
    
        return (
          <div>
            {currentNavIndex}<br/>
            <button onClick={this.changeTabBar.bind(this, currentNavIndex + 1)}> click me </button>
          </div>
        )
      }
    }
    
    export default Demo
    
    // 输出结果如下
    // Demo click
    // CounterSCU render count = 18
    // CounterSCU render count = 19
    // CounterPure render count = 14
    

    引用

    React.PureComponent is similar to React.Component. The difference between them is that React.Component doesn’t implement shouldComponentUpdate(), but React.PureComponent implements it with a shallow prop and state comparison.

    结论

    比较下来发现 PureComponent 只对当前组件上的 props 和内部的 state受影响,而且 props 上变化只做浅比较,它和 Component 使用 shouldComponentUpdate 的用法一致,使用 PuerCompoent 确实对于 render 性能有提升。

    相关文章

      网友评论

        本文标题:记录一些 PureComponent 的用法

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