React基础 - 解决组件频繁更新

  • React的组件化中,我们通常将组件分为容器组件、展示型组件,原则上展示型组件尽量不处理逻辑,所有的属性、事件都放在容器组件中处理
  • 初始化一个留言板组件,并在 App.js 中引入使用
    初始化留言板的展示.jpg
// src/components/CommentList.js
import React from 'react'

export class CommentList extends React.Component {
  constructor(props) {
    super(props)
    this.state = {
      comments: []
    }
  }

  componentDidMount () {
   // 组件加载完之后赋值数据
    setTimeout(() => {
      this.setState({
        comments: [
          {body: 'react is very good', author: 'facebook'},
          {body: 'vue is very good', author: 'youyuxi'}
        ]
      })
    }, 1000)
  }

  render () {
    return (
      
{/* 遍历展示留言 */} {this.state.comments.map((c, i) => ( ))}
) } } // 留言组件,是个纯展示组件,展示留言内容及留言发布者 class Comment extends React.Component { render () { return (

{this.props.data.body}

-- {this.props.data.author}

) } }
  • Comment 组件打印该组件 render 的情况
class Comment extends React.Component {
  render () {
    // 在此处打印render的执行情况,从控制台能看出这里执行了4次render
    // 首先因为严格模式,每次被触发执行render都会被执行2次,所以这里触发2次render的执行,分别是首次加载、setTimeout中的setState
    console.log('Comment render')
    return (
      

{this.props.data.body}

-- {this.props.data.author}

) } }
  • 从上面执行 render 的情况可以看出,每次父组件数据更新都会触发 Commentrender ,这样就存在不合理的情况,假设如下:
// 假设目前的留言板需要做轮询更新,实时更新留言板的留言数据
// 那么每一次轮询都会触发Comment的render, Comment每一次都要做DOM对比
// 然后当留言板数据更新频率不高时,也就是有时候留言数据并没有更新,但是每一次轮询却都会触发Comment组件的render
  componentDidMount () {
    setInterval(() => {
      this.setState({
        comments: [
          {body: 'react is very good', author: 'facebook'},
          {body: 'vue is very good', author: 'youyuxi'}
        ]
      })
    }, 1000)
  }
  • 所以下一步需要解决组件频繁更新的问题,组件频繁的更新、每一次更新所做的DOM对比,对性能都是一种消耗
// 避免组件频繁更新1 - shouldComponentUpdate
// 该方法较为繁琐,也不太可能在代码里对比状态的每一个属性

class Comment extends React.Component {

  shouldComponentUpdate (nextProps) {
    // nextProps是接下来的新状态,被改变之后的状态
    // 在shouldComponentUpdate生命周期内对比改变之后的状态与之前额状态是否一致
    if(nextProps.data.body === this.props.data.body &&
      nextProps.data.author === this.props.data.author) {
        return false;
    }
    return true;
  }

  render () {
    // 打印4次
    console.log('Comment render')
    return (
      

{this.props.data.body}

-- {this.props.data.author}

) } }
// 避免组件频繁更新2 - PureComponent 
// 更换继承类为PureComponent,PureComponent的原理是内置shouldComponentUpdate并对shouldComponentUpdate做了一定扩展

class Comment extends React.PureComponent {
  render () {
    // 每秒打印2次
    console.log('Comment render')
    return (
      

{this.props.data.body}

-- {this.props.data.author}

) } } // 写到这里会发现,控制台并没有停止打印 “Comment render”,依然保持每一秒打印一次 // 这是因为PureComponent只做浅比较,不会递归比较属性,在对比对象的时候只会比较对象的引用,或者只比较最外层属性 // 我们这里的每一次 setState 都是对引用的更新,所以每一次对比上一次状态,永远都是新的数据,因此Comment组件会一直render // 所以在使用PureComponent时尽量避免对象类型数据,尽可能只用在值类型数据,或者使用一层属性的对象
// 避免组件频繁更新2 - 优化 - PureComponent 

// 首先优化父组件传值的方式,将原本传的对象c扩展开
render () {
   return (
     
{this.state.comments.map((c, i) => ( // 这里扩展相当于: ))}
) } // 然后优化子组件获取属性的写法 class Comment extends React.PureComponent { render () { // 打印4次 console.log('Comment render') return (

{this.props.body}

-- {this.props.author}

) } }
// 避免组件频繁更新3 - React.memo v16.6.0后的版本才有
// memo是一个高阶组件,是一个函数,接收一个组件返回一个新组件
// 类似PureComponent只做浅比较,解决了函数型组件没有PureComponent功能的问题
const Comment = React.memo(function (props) {
  // 打印2次
  console.log('Comment render')
  return (
    

{props.body}

-- {props.author}

) })

你可能感兴趣的:(React基础 - 解决组件频繁更新)