1. 首页
  2. IT资讯

一文掌握React性能优化

1 shouldeComponentUpdate

shouldComponentUpdate应该是提到的优化方法里面提到最多的,使用的也是最多的,如果返回false,就会阻止下面组件的渲染,反之,返回true,就会进行接下来的渲染,默认返回的事true。但是有个问题,shouldComponentUpdate进行的是浅比较,为什么不进行深比较,因为伤不起,深比较的耗费性能可能比每次render的效率还要低。

class Nest extends React.Component {   shouldComponentUpdate = (nextProps, nextState) => {     return (       !shallowEqual(this.props, nextProps) ||       !shallowEqual(this.state, nextState)     )   }   render() {     console.log('inner')     return <div>Nest</div>   } } 

为什么进行的事浅比较,我们从源码方面来看一下:

const hasOwn = Object.prototype.hasOwnProperty  function is(x, y) {   if (x === y) {     return x !== 0 || y !== 0 || 1 / x === 1 / y   } else {     return x !== x && y !== y   } }  export default function shallowEqual(objA, objB) {   if (is(objA, objB)) return true    if (typeof objA !== 'object' || objA === null ||       typeof objB !== 'object' || objB === null) {     return false   }    const keysA = Object.keys(objA)   const keysB = Object.keys(objB)    if (keysA.length !== keysB.length) return false    for (let i = 0; i < keysA.length; i++) {     if (!hasOwn.call(objB, keysA[i]) ||         !is(objA[keysA[i]], objB[keysA[i]])) {       return false     }   }    return true } 

首先是Object.is的Polyfill,为什么要用这个来比较而不是 == 或者 === 呢?

使用 == 会 出现以下bug:

0 == ' '  // true null == undefined // true [1] == true // true 

使用 === 会出现以下bug:

+0 === -0 // true, NaN === NaN // false, 

Object.is修复了=== 这两种判断不符合预期的情况,并null,undefined,number,string,boolean做出非常精确的比较,以下几种情况都会返回true。

  • 两个值都是 undefined
  • 两个值都是 null
  • 两个值都是 true 或者都是 false
  • 两个值是由相同个数的字符按照相同的顺序组成的字符串
  • 两个值指向同一个对象
  • 两个值都是数字并且
    • 都是正零 +0
    • 都是负零 -0
    • 都是 NaN
    • 都是除零和 NaN 外的其它同一个数字

当对比的类型为Object的时候并且key的长度相等的时候,浅比较也仅仅是用Object.is()对Object的value做了一个基本数据类型的比较,所以如果key里面是对象的话,就不能进行比较。

const hasOwn = Object.prototype.hasOwnProperty // 下面就是进行浅比较了, 有不了解的可以提issue, 可以写一篇对比的文章。 function is(x, y) {   // === 严格判断适用于对象和原始类型。但是有个例外,就是NaN。   if (x === y) {     //这个是个例外,为了针对0的不同,譬如 -0 === 0 => true     // (1 / x) === (1 / y)这个就比较有意思,可以区分正负0, 1 / 0 => Infinity, 1 / -0 => -Infinity     return x !== 0 || y !== 0 || 1 / x === 1 / y    } else {     // 这个就是针对上面的NaN的情况     return x !== x && y !== y   } }  export default function shallowEqual(objA, objB) {   if (is(objA, objB)) return true //这个就是实行了Object.is的功能。实行的是SameValue策略。   // is方法之后,我们认为他不相等。不相等的情况就是排除了(+-0, NaN)的情况以及可以证明:   // 原始类型而言: 两个不是同类型或者两个同类型,值不同。   // 对象类型而言: 两个对象的引用不同。       //下面这个就是,如果objA和objB其中有个不是对象或者有一个是null, 那就认为不相等。   //不是对象,或者是null.我们可以根据上面的排除来猜想是哪些情况:   //有个不是对象类型或者有个是null,那么我们就直接返回,认为他不同。其主要目的是为了确保两个都是对象,并且不是null。   if (typeof objA !== 'object' || objA === null ||       typeof objB !== 'object' || objB === null) {     return false   }    //如果上面没有返回,那么接下来的objA和objB都是对象了。    const keysA = Object.keys(objA)   const keysB = Object.keys(objB)    //两个对象不同,有可能是引用不同,但是里面的内容却是相同的。例如:{a: 'a'} ==~ {a: 'a'}   //所以先简单粗暴的判断一级的keys是不是相同的长度。,不是那就肯定不相等,就返回false。   if (keysA.length !== keysB.length) return false    //下面就是判断相同长度的key了   // 可以发现,遍历的是objA的keysA。   //首先判断objB是否包含objA的key,没有就返回false。注意这个是采用的hasOwnPrperty来判断,可以应付大部分的情况。   //如果objA的key也在ObjB的key里,那就继续判断key对应的value,采用is来对比。哦,可以发现,只会对比到第以及。    for (let i = 0; i < keysA.length; i++) {     if (!hasOwn.call(objB, keysA[i]) ||         !is(objA[keysA[i]], objB[keysA[i]])) {       return false     }   }    return true } 

2 React.PureComponent

个人觉得此优化手段适用于数据变化不太频繁,如果只有一个或者展示类的组件可以使用,因为在父组件进行重新render时候,可以有效避免利用PureComponent的浅比较避免组件的渲染,不可以有每次都会变动的值,因为这样你的 PureComponent 和 Component 其实没两样。但是一下几个小tip,可能避免组件重新render,其实这样的点有很多,只是把我遇到的全部列举出来。

  • props为空数组
<CustomList data={data || []} /> 

CustomList 在 data 值为 null 或 undefined 时,仍不会发生奔溃,但如果你这么做,你会发现即使每次传入的 props 都是[],仍然会发生 render ,原因在于[] !== [],他们的 reference 并不相同。解决办法: 设置defaultProps 默认值。

  • props 为 inline function
<CustomList  clickHandler={ () => this.setState({number: this.state.number + 1}) } /> 

这样 每次点击时,都会传入一个新的function,又因为reference不同,导致每次都会渲染。
解决办法: 将内连的function,进行预定义,拆出来定义。

  • props 使用的function类型 例如:
 1 <CustomList clickHandler={this.handleClick } />   // constructor里面绑定this;  2 <CustomList clickHandler={this.handleClick.bind(this) } />   3 <CustomList clickHandler={()=>this.handleClick.bind() } /> 

方式一: 构造函数每一次渲染的时候只会执行一遍; 性能最好;

方式二: 在每次render()的时候都会重新执行一遍函数;

方式三: 每一次render()的时候,都会生成一个新的箭头函数,即使两个箭头函数的内容是一样的,因为react判断是否需要进行render是浅层比较,简单来说就是通过===来判断的,如果state或者prop的类型是字符串或者数字,只要值相同,那么浅层比较就会认为其相同

当你使用 PureComponent 時,如果 props 或 state 要变动,可以尝试使用 Immutable.js 来处理,避免有改了却沒重新 render 的情况发生。


3. immutable.js

Immutable提供一直简单快捷的方式以判断对象是否变更,对于React组件更新和重新渲染性能可以有较大帮助。immutable可以几乎在全家桶中结合,打算专门写一篇介绍。

import { is } from 'immutable';  shouldComponentUpdate: (nextProps = {}, nextState = {}) => {   const props = this.props || {}, state = this.state || {};    if (Object.keys(props).length !== Object.keys(nextProps).length ||       Object.keys(state).length !== Object.keys(nextState).length) {     return true;   }    for (const key in nextProps) {     if (!is(props[key], nextProps[key])) {       return true;     }   }    for (const key in nextState) {     if (state[key] !== nextState[key] || !is(state[key], nextState[key])) {       return true;     }   }   return false; } 

4.单组件中拆分组件,尽量避免嵌套方法,一串传参下去,导致一参数改变,一串函数都会重新render;

5.多组件同样也应该避免从父级获取store数据传给子组件,会导致父组件中的子组件某个数据改变,导致父组件中所有子组件都会改变,全部重新render;

6. 未完待续。。。。

原文始发于:一文掌握React性能优化

主题测试文章,只做测试使用。发布者:酒颂,转转请注明出处:http://www.cxybcw.com/25699.html

联系我们

13687733322

在线咨询:点击这里给我发消息

邮件:1877088071@qq.com

工作时间:周一至周五,9:30-18:30,节假日休息

QR code