React 性能优化

说起 react 的性能优化,首先,高效的虚拟 DOM diff 算法和 react 内部的引擎已经做的很完美了,一般项目已经够用了,对性能的需求要求并不大,但是对于复杂的项目,性能优化还是很有必要的,

性能到底指的是什么?

前端开发,自然是离不开浏览器,而前端的性能优化大多都是在和浏览器打交道,页面的每一帧变化都是有浏览器绘制出来的,而且这个绘制的频率受限于显示器的刷新频率,所以一个重要的性能数据指标是每秒 60 帧的绘制频率,绘制的每一帧大概是需要 16.6ms。

如果一个应用对用户的交互响应处理过慢,需要花费很长的时间来计算更新数据,这会给用户带来很差的用户体验,

但是对于 react 来说,开发者不需要额外关注 DOM 层面的操作,因为 react 通过维护虚拟的 Dom 和高效的 diff 算法,可以决策出每次最小化的更新 DOM,我们编写的 React 组件都是处于 javascript 层面的,而我们的虚拟 Dom 全是由 JavaScript 对象表示的,所以说 react 已经最大程度的避免了与 Dom 的操作。

1. React 自身虚拟 DOM 带来的优化

1.1 原始 DOM 操作

当我们在进行DOM操作的时候:

document.getElementById('app').innerHTML = 'new value'

这一个过程要经过的阶段有:

  1. 浏览器解析 html
  2. 删除之前的id为app的元素
  3. 使用新的 new value 更新dom
  4. 为相关的节点重新计算 css 样式
  5. 在显示屏上完成新的布局或绘制
  6. 在渲染树中进行新的样式绘制

在上面的操作中,重新更新dom和计算css对性能的影响是非常大的,每一次更新都要重新触发上面的过程,代价比较大。

1.2 虚拟 DOM 操作

为了避免操作 DOM 而带来的性能上的付出和隐患,react 不建议开发者直接更新真实的dom节点,而是维护着一套虚拟的 Dom,通过 diff 算法手段触发更新 (设计虚拟dom不全是为了提高性能)

其实虚拟的Dom在内存中维护着一个真实的 DOM ,它是由 javascript 对象描述的
当任何一个组件使用setSate方法时,react都是认为该组件变"脏"了,触发组件重新渲染,因为 react 内部维护着两套虚拟的dom,一套更新后的虚拟 dom,一套是更新前的虚拟 dom,通过这两套虚拟的 dom运用diff算法,找到需要变化的最小单元集,然后把这个最小单元集运用到真实的 dom 中。

2 最大限度的减少重新渲染 – shouldComponentUpdate

shouldComponentUpdate 生命周期函数是通过对比前后 state/props 是否发生变更,来决定组件是否要重新渲染

shouldComponentUpdate(nextProps, nextState) {
   return nextState.someData !== this.state.someData 
}

3 使用 PureComponent 保证开发性能

在 React 15.3 的版本中具有了React.PureComponent 特性,其实 PureComponent 与Component 大致相同,唯一不同的是 PureComponent 会自动帮助开发者使用 shouldComponentUpdate 生命周期函数,也就是说,当组件的 state 和 props 发生变化的时候,正常的component组件会自动进行重新渲染,在这种情况下 shouldComponentUpdate自动都会返回 true,但是 PureComponent 组件中会先进行对比,比较两次的 state 或者 props 是否相同,然后再决定是否要重新渲染
通过代码:总结到使用 PureComponent 应该注意到以下两点

  • 既然是浅比较,当与前一个state或者props进行比较的时候,如果比较的是javascript基本类型,那么会对其值判断是否相等,如果比较的是复杂数据类型(array,object),那么会判断引用是否相等,不会对值进行比较,
下面的三种组件声明的方式进行对比
//1. 函数式方式
export const function1 = ({name,age,hobby}) =>{
  return (
      <>
          <div>{age}</div>
          <div>{name}</div>
          <div>{hobby}</div>
      </>
      )
}

// 2. PureComponent 方式
export class PureComponent extends React.PureComponent{
  render(){
    const {name,age,hobby} = this.props
    return (
        <>
            <div>{age}</div>
            <div>{name}</div>
            <div>{hobby}</div>
        </>
       )
  }
  
}
// 经典class方式
export class Component extends React.Component{
  render(){
    const {name,age,hobby} = this.props
    return (
          <>
              <div>{age}</div>
              <div>{name}</div>
              <div>{hobby}</div>
          </>
        )
  }
  
}

在使用 PureComponent 声明的组件中,会自动进行前后 {name,age,hobby} 的对比,如果没有发生变化 shouldComponentUpdate 会自动返回 false,因此 PureComponent 的性能明显高于其他的组件,但是 PureComponent 不是万能的,因为 PureComponent 内部进行的是浅比较,要根据开发的需求,在其他情况下可以用 shouldComponentUpdate 来实现视图的渲染与否。

4 inline function 的反模式

什么叫反模式,比如,当我们在使用 render 方法的时候,要留意在render方法内部使用的函数或者数组,这些创建的操作很可能是显式的,也可能是隐式的,因为这些新生的函数或者数组,当量较大的时候也会造成一定的性能负担,render 方法被执行多次,总有新的方法或者数组被创建,这样会造成内存无意义的开销,通常我们的做法是只需要让他们创建一次,比如:

render(){
   return <MyInput onChange = {this.props.update.bind(this)}>
}

 // 或者

render(){
   return <MyInput onChange = {()=>this.props.update()}>
}

我们在使用 bind 方法的时候每次创建都会创建一个新的函数,对内存造成了不必要的消耗,我们可以:

onChange(){
   this.props.update()
}
render(){
   return <MyInput onChange = {this.onChage()}>
}

或者还可以在 constructor 中先定义一下

constructor(props){
  super(props)
  this.update = this.props.update.bind(this)
}
render(){
   return <MyInput onChange = {this.update()}>
}

比如在render中使用数据的情况下

render(){
   return <MyInput items = {this.props.items} || []>
}

这样在每次 this.props.items 不存在的时候都会创建一个空数组,正确的用法:

render(){
    const arr = []
    return <MyInput items = {this.props.items} || arr>
}

还有我们从state中取值的时候也要尽量先取出来再使用

render(){
    return <div>{this.state.name}</div>
}
// 正确的做法
render(){
    const {name} = this.state
    return <div>{name}</div>
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值