美文网首页
React基础 - 解决组件频繁更新

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

作者: 闪电西兰花 | 来源:发表于2020-07-27 15:53 被阅读0次
  • 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 (
      <div>
        {/* 遍历展示留言 */}
        {this.state.comments.map((c, i) => (
          <Comment key={i} data={c}></Comment>
        ))}
      </div>
    )
  }
}

// 留言组件,是个纯展示组件,展示留言内容及留言发布者
class Comment extends React.Component {
  render () {
    return (
      <div>
        <p>{this.props.data.body}</p>
        <p>-- {this.props.data.author}</p>
      </div>
    )
  }
}
  • Comment 组件打印该组件 render 的情况
class Comment extends React.Component {
  render () {
    // 在此处打印render的执行情况,从控制台能看出这里执行了4次render
    // 首先因为严格模式,每次被触发执行render都会被执行2次,所以这里触发2次render的执行,分别是首次加载、setTimeout中的setState
    console.log('Comment render')
    return (
      <div>
        <p>{this.props.data.body}</p>
        <p>-- {this.props.data.author}</p>
      </div>
    )
  }
}
  • 从上面执行 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 (
      <div>
        <p>{this.props.data.body}</p>
        <p>-- {this.props.data.author}</p>
      </div>
    )
  }
}
// 避免组件频繁更新2 - PureComponent 
// 更换继承类为PureComponent,PureComponent的原理是内置shouldComponentUpdate并对shouldComponentUpdate做了一定扩展

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

// 首先优化父组件传值的方式,将原本传的对象c扩展开
render () {
   return (
     <div>
       {this.state.comments.map((c, i) => (
         // 这里扩展相当于: <Comment key={i} body={c.body} author={c.author}></Comment>
         <Comment key={i} {...c}></Comment>
       ))}
     </div>
   )
 }

// 然后优化子组件获取属性的写法
class Comment extends React.PureComponent {
  render () {
    // 打印4次
    console.log('Comment render')
    return (
      <div>
        <p>{this.props.body}</p>
        <p>-- {this.props.author}</p>
      </div>
    )
  }
}
// 避免组件频繁更新3 - React.memo v16.6.0后的版本才有
// memo是一个高阶组件,是一个函数,接收一个组件返回一个新组件
// 类似PureComponent只做浅比较,解决了函数型组件没有PureComponent功能的问题
const Comment = React.memo(function (props) {
  // 打印2次
  console.log('Comment render')
  return (
    <div>
      <p>{props.body}</p>
      <p>-- {props.author}</p>
    </div>
  )
})

相关文章

网友评论

      本文标题:React基础 - 解决组件频繁更新

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