React性能优化之SCU

一、概念

shouldComponentUpdate简称SCU,是React中性能优化的重要一环。

shouldComponentUpdate(nextProps, nextState) {
    // 判断是否需要被渲染,如果需要则返回true,否则返回false 
    if (nextProps.b === this.props.b) {
        return false;
    } else {
        return true;
    }
}

二、使用场景

react同一父组件的所有子组件中有一个更新,剩下的所有子组件都会重新渲染,但是为了性能,我们不需要让未更新依赖的组件更新。SCU就是解决这个未更新依赖的组件的重新渲染问题。

三、SCU实战(浅比较已适用大部分情况,尽量不要深度比较)

1、基本使用方法

例1:

import React from 'react'

class App extends React.Component {
  constructor(props) {
      super(props)
      this.state = {
          count: 0
      }
  }
  render() {
      return <div>
          <span>{this.state.count}</span>
          <button onClick={this.onIncrease}>increase</button>
      </div>
  }
  onIncrease = () => {
      this.setState({
          count: this.state.count + 1
      })
  }
  // 演示 shouldComponentUpdate 的基本使用
  shouldComponentUpdate(nextProps, nextState) {
      if (nextState.count !== this.state.count) {
          return true // 可以渲染
      }
      return false // 不重复渲染
  }
}

export default App

例1:

import React from 'react'
import PropTypes from 'prop-types'

class Input extends React.Component {
    constructor(props) {
        super(props)
        this.state = {
            title: ''
        }
    }
    render() {
        return <div>
            <input value={this.state.title} onChange={this.onTitleChange}/>
            <button onClick={this.onSubmit}>提交</button>
        </div>
    }
    onTitleChange = (e) => {
        this.setState({
            title: e.target.value
        })
    }
    onSubmit = () => {
        const { submitTitle } = this.props
        submitTitle(this.state.title) // 'abc'

        this.setState({
            title: ''
        })
    }
}
// props 类型检查
Input.propTypes = {
    submitTitle: PropTypes.func.isRequired
}

class List extends React.Component {
    constructor(props) {
        super(props)
    }
    render() {
        const { list } = this.props

        return <ul>{list.map((item, index) => {
            return <li key={item.id}>
                <span>{item.title}</span>
            </li>
        })}</ul>
    }
}
// props 类型检查
List.propTypes = {
    list: PropTypes.arrayOf(PropTypes.object).isRequired
}

class Footer extends React.Component {
    constructor(props) {
        super(props)
    }
    render() {
        return <p>
            {this.props.text}
            {this.props.length}
        </p>
    }
    componentDidUpdate() {
        console.log('footer did update')
    }
    shouldComponentUpdate(nextProps, nextState) {
      console.log(nextProps, this.props)
        if (nextProps.text !== this.props.text) { //  || nextProps.length !== this.props.length
          return true // 可以渲染
        }
        return false // 不重复渲染
    }

    // React 默认:父组件有更新,子组件则无条件也更新!!!
    // 性能优化对于 React 更加重要!
    // SCU 一定要每次都用吗?—— 需要的时候才优化
}

class TodoListDemo extends React.Component {
    constructor(props) {
        super(props)
        // 状态(数据)提升
        this.state = {
            list: [
                {
                    id: 'id-1',
                    title: '标题1'
                },
                {
                    id: 'id-2',
                    title: '标题2'
                },
                {
                    id: 'id-3',
                    title: '标题3'
                }
            ],
            footerInfo: '底部文字'
        }
    }
    render() {
        return <div>
            <Input submitTitle={this.onSubmitTitle}/>
            <List list={this.state.list}/>
            <Footer text={this.state.footerInfo} length={this.state.list.length}/>
        </div>
    }
    onSubmitTitle = (title) => {
        this.setState({
            list: this.state.list.concat({
                id: `id-${Date.now()}`,
                title
            })
        })
    }
}

export default TodoListDemo 

例3:

import React from 'react'
import PropTypes from 'prop-types'
import _ from 'lodash'

class Input extends React.Component {
    constructor(props) {
        super(props)
        this.state = {
            title: ''
        }
    }
    render() {
        return <div>
            <input value={this.state.title} onChange={this.onTitleChange}/>
            <button onClick={this.onSubmit}>提交</button>
        </div>
    }
    onTitleChange = (e) => {
        this.setState({
            title: e.target.value
        })
    }
    onSubmit = () => {
        const { submitTitle } = this.props
        submitTitle(this.state.title)

        this.setState({
            title: ''
        })
    }
}
// props 类型检查
Input.propTypes = {
    submitTitle: PropTypes.func.isRequired
}

class List extends React.Component {
  constructor(props) {
      super(props)
  }
  render() {
    const { list } = this.props

    return <ul>{list.map((item, index) => {
        return <li key={item.id}>
            <span>{item.title}</span>
        </li>
    })}</ul>
  }
  componentDidUpdate() {
    console.log('List did update')
  }

  // 增加 shouldComponentUpdate
  shouldComponentUpdate(nextProps, nextState) {
    console.log(nextProps.list, this.props.list)
      // _.isEqual 做对象或者数组的深度比较(一次性递归到底)
      if (_.isEqual(nextProps.list, this.props.list)) {
          // 相等,则不重复渲染
          return false
      }
      return true // 不相等,则渲染
  }
}
// props 类型检查
List.propTypes = {
    list: PropTypes.arrayOf(PropTypes.object).isRequired
}

class TodoListDemo extends React.Component {
  constructor(props) {
      super(props)
      this.state = {
          list: [
              {
                  id: 'id-1',
                  title: '标题1'
              },
              {
                  id: 'id-2',
                  title: '标题2'
              },
              {
                  id: 'id-3',
                  title: '标题3'
              }
          ]
      }
  }
  render() {
      return <div>
          <Input submitTitle={this.onSubmitTitle}/>
          <List list={this.state.list}/>
      </div>
  }
  onSubmitTitle = (title) => {
      // 正确的用法
      this.setState({
          list: this.state.list.concat({
              id: `id-${Date.now()}`,
              title
          })
      })

      // // 为了演示 SCU ,故意写的错误用法   SCU要配合不可变值
      // this.state.list.push({      // 已经改了 再setState都一样了 nextProps === this.props了  SCU返回false就不更新了
      //     id: `id-${Date.now()}`,
      //     title
      // })
      // this.setState({
      //     list: this.state.list
      // })
  }
}

export default TodoListDemo

React性能优化之SCU_第1张图片

2、PureComponent方法(类组件)

类组件 extends pureComponent的时候 当前组件也是浅比较的scu
React性能优化之SCU_第2张图片

例1:

import React from 'react'
import PropTypes from 'prop-types'

class Input extends React.Component {
  constructor(props) {
      super(props)
      this.state = {
          title: ''
      }
  }
  render() {
    return <div>
      <input value={this.state.title} onChange={this.onTitleChange}/>
      <button onClick={this.onSubmit}>提交</button>
    </div>
  }
  onTitleChange = (e) => {
    this.setState({
        title: e.target.value
    })
  }
  onSubmit = () => {
    const { submitTitle } = this.props
    submitTitle(this.state.title)

    this.setState({
        title: ''
    })
  }
}
// props 类型检查
Input.propTypes = {
  submitTitle: PropTypes.func.isRequired
}

class List extends React.PureComponent {
  constructor(props) {
      super(props)
  }
  componentDidUpdate() {
    console.log('List did update')
  }
  render() {
    const { list } = this.props

    return <ul>{list.map((item, index) => {
        return <li key={item.id}>
            <span>{item.title}</span>
        </li>
    })}</ul>
  }
  // shouldComponentUpdate() {/*浅比较*/}  其实就相当于加了这个 做浅比较
}
// props 类型检查
List.propTypes = {
  list: PropTypes.arrayOf(PropTypes.object).isRequired
}

class TodoListDemo extends React.Component {
  constructor(props) {
    super(props)
    this.state = {
      list: [
          {
              id: 'id-1',
              title: '标题1'
          },
          {
              id: 'id-2',
              title: '标题2'
          },
          {
              id: 'id-3',
              title: '标题3'
          }
      ]
    }
  }
  render() {
    return <div>
        <Input submitTitle={this.onSubmitTitle}/>
        <List list={this.state.list}/>
    </div>
  }
  onSubmitTitle = (title) => {
    // 正确的用法
    this.setState({
        list: this.state.list.concat({
            id: `id-${Date.now()}`,
            title
        })
    })

    // // 为了演示 SCU ,故意写的错误用法
    // this.state.list.push({
    //     id: `id-${Date.now()}`,
    //     title
    // })
    // this.setState({
    //     list: this.state.list
    // })
  }
}

export default TodoListDemo

3、React.memo高阶组件(函数组件)

React.memo 这个高阶函数 可以叫高阶组件
React.memo 这个高阶组件做scu 也是浅比较的scu
React性能优化之SCU_第3张图片

四、性能优化小结

immutable.js
React性能优化之SCU_第4张图片
React性能优化之SCU_第5张图片

React性能优化之SCU_第6张图片

你可能感兴趣的:(React,react.js,javascript,前端)