React中的Component 和 PureComponent

React.ComponentReact.PureComponent很相似,两则的区别在于,PureComponent类帮我们以浅比较的方式对比propsstate,实现了shouldComponentUpdate()函数,在某些情况下,使用PureComponent可以减少render函数的执行,提升性能。

PureComponent能够提升性能

当Index组件继承Component

  1. 初次渲染时控制台会依次打印"constructor"、"render";

  2. 当第一次点击按钮更新state时,控制台会依次打印"render"、"componentDidUpdate";

  3. 后续每次触发点击事件,尽管flag的值没有变化,控制台还是会依次打印"render"、"componentDidUpdate",说明组件依然调用render()componentDidUpdate()函数,显然这是多余的,通常我们会手动重新实现shouldComponentUpdate(nextProps, nextState)函数判断stateprops的状态再来决定是否需要重新渲染

import React from 'react';

class Index extends React.PureComponent{
  constructor(props) {
    super(props);
    this.state = {
      flag:false
    };
    console.log('constructor');
  }
  changeState = () => {
    this.setState({
      flag: true
    })
  };
  render() {
    console.log('render');
    return (
      <div>
        <button onClick={this.changeState}>Click me</button>
        <div>
         {this.state.flag.toString()}
        </div>
      </div>
    );
  }
  componentDidUpdate() {
    console.log("componentDidUpdate")
  }
}

export default Index;

当Index组件继承PureComponent

  • 初次渲染和第一次点击按钮更新state时控制台输出同上面继承Component一样没有变化

  • 后续每次触发点击事件,控制台无输出, 省去执行render函数生成虚拟DOM,进行DIFF算法比较等后续操作

if (this._compositeType === CompositeTypes.PureClass) {
  shouldUpdate = !shallowEqual(prevProps, nextProps) || !shallowEqual(inst.state, nextState);
}

PureComponent默认实现的shouldComponentUpdate()方法使用的是浅比较: 即值的比较或引用的比较, 不会进行深层次的对比,所以当propsstate的值是引用类型时,即使对象的值改变了,但是对象的引用没变

import React from 'react';

class IndexPage extends React.PureComponent{
  constructor(props) {
    super(props);
    this.state = {
      arr: [1,2,3,4,5]
    };
  }
  changeArr = () => {
    let {arr} = this.state
    arr.pop()
    this.setState({
      arr
    })
    console.log("changeArr", arr)
  };
  render() {
    const { arr } = this.state
    console.log('render', arr);
    return (
      <div>
        <button onClick={this.changeArr}>pop</button>
        <ul>
        {
          arr.map(item => <li key={item}>{item}</li>)
        }
        </ul>
      </div>
    );
  }
  componentDidUpdate() {
    console.log("componentDidUpdate")
  }
}

export default IndexPage;
  1. 初次渲染时控制台会打印 render (5) [1, 2, 3, 4, 5]
  2. 当点击pop按钮时控制台会依次打印 changeArr (4) [1, 2, 3, 4]changeArr (4) [1, 2, 3] ...... 但是render函数不执行, 因为PureComponent实现的shouldComponentUpdate()认为值的引用没有变,故不执行后续的操作,只有在引用改变的情况下函数才会返回true

PureComponent也会影响子组件

下面例子中的render函数只会在刚创建的时候执行一次, 后续的点击按钮操作,由于PureComponent中的ShouldComponentUpdate()执行浅比较(对象值的引用没变),不会触发render函数的执行,其子组件也不会更新。

import React from 'react';
import Item from './Item'

class IndexPage extends React.PureComponent{
  constructor(props) {
    super(props);
    this.state = {
      arr: [1,2,3,4,5]
    };
  }
  changeArr = () => {
    let {arr} = this.state
    arr.pop()
    this.setState({
      arr
    })
    console.log("changeArr", arr)
  };
  render() {
    const { arr } = this.state
    console.log('render', arr);
    return (
      <div>
        <button onClick={this.changeArr}>pop</button>
        <ul>
          <Item arr={arr} />
        </ul>
      </div>
    );
  }
  componentDidUpdate() {
    console.log("componentDidUpdate")
  }
}

export default IndexPage;
// Item.js
import React, { Fragment, Component } from 'react'

class Index extends Component {
  render() {
    const { arr } = this.props;
    console.log("children", arr)
    return (
      <Fragment>
        {
          arr.map(item => <li key={item}>{item}</li>)
        }
      </Fragment>
    )
  }
}

export default Index;

总结

  1. PureComponent已经用浅层对比propsstate的方式替我们实现了shouldComponentUpdate(), 不仅能影响自身,还会影响其子组件;
  2. PureComponent 某些情况下(propsstate的值不经常变动, 因为浅比较也会耗时)可以提升性能;
  3. 继承自Component中的组件shouldComponentUpdate()默认情况下总是返回true;