React Native 性能优化组件-PureComponen

作者: 慕久久 | 来源:发表于2018-12-11 15:42 被阅读26次

    背景

    React 使用了 PureComponent,在 shouldComponentUpdate 进行浅比较,这里到底什么是浅比较呢?

    shallowEqual

    在React里,shouldComponentUpdate源码为:

    if (this._compositeType === CompositeTypes.PureClass) {  
      shouldUpdate = !shallowEqual(prevProps, nextProps) || !shallowEqual(inst.state, nextState);
    }
    

    可以看到PureComponent就是对props跟state的前后状态做了一个浅比较。

    看看shallowEqual的源码:

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

    Object.is()

    在解析 shallowEqual的源码之前,先来认识一下 Object.is(),这个函数是用来比较两个值是否相等。

    为什么要用这个来比较而不是 == 或者 === 呢?

    ==

    首先先看 ==,由于 JS 是弱类型的,如果使用 == 进行比较,== 操作符会自动将 0,‘’(空字符串),null,undefined 转成布尔型 false,这样就会出现

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

    这显然是不符合预期的。所以 JS 为我们提供了全等操作符 ===,它不会进行类型转换,也就是说如果两个值一样,必须符合类型也一样。但是,它还是有两种疏漏的情况

    +0 === -0//true,但我们期待它返回falseNaN===NaN//false,我们期待它返回true
    

    所以,Object.is() 修复了 `=== 这两种判断不符合预期的情况,

    function (x, y){
      // SameValue algorithm
      if(x === y) {
        // 处理为+0 != -0的情况
        returnx !==0 || 1/ x ===1/ y;    
      } else {
        // 处理 NaN === NaN的情况
        return x !== x && y !== y;    
      }
    };
    

    这样就使得 Object.is() 总是返回我们需要的结果。 它在下面6种情况下,会返回 true

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

    可以看出Object.is 可以对基本数据类型: null,undefined,number,string,boolean做出非常精确的比较,但是对于引用数据类型是没办法直接比较的。

    剖析 shallowEquall

    // 用原型链的方法
    const hasOwn = Object.prototype.hasOwnProperty
    // 这个函数实际上是 Object.is() 的 polyfill
    functionis (x, y) {
      if (x === y) {
        returnx !==0 || y !==0 ||1/ x === 1/ y  
      } else {
        returnx !== x && y !== y  
      }
    } 
    export default function shallowEqual (objA, objB) {
      // 首先对基本数据类型的比较
      if (is(objA, objB))
        return true
      // 由于 Obejct.is() 可以对基本数据类型做一个精确的比较, 所以如果不等
      // 只有一种情况是误判的,那就是 object, 所以在判断两个对象都不是 object
      // 之后,就可以返回 false 了
      if (typeofobjA !== 'object' || objA === null || typeofobjB !== 'object' || objB ===null ) {
        return false
      }
      // 过滤掉基本数据类型之后,就是对对象的比较了
      // 首先拿出 key 值,对 key 的长度进行对比 
      const keysA = Object.keys( objA)
      const keysB = Object.keys(objB)  
      // 长度不等直接返回 false 
      if (keysA.length !== keysB.length)
        return false
      // key 相等的情况下,在去循环比较
      for  (let i =0; i < keysA.length; i++) {
        // key值相等的时候
        // 借用原型链上真正的 hasOwnProperty 方法,判断ObjB里面是否有A的key的key值
        // 属性的顺序不影响结果也就是 {name:'daisy', age:'24'} 跟 {age:'24',name:'daisy' } 是一样的
        // 最后,对对象的value进行一个基本数据类型的比较,返回结果
        if( !hasOwn.call(objB, keysA[i])  || !is(objA[keysA[i]], objB[keysA[i]])){
          return false
         }  
      }
      return true
    }
    

    总结

    回到最开始的问题,浅比较为什么没办法对嵌套的对象比较?

    由上面的分析可以看到,当对比的类型为Object的时候并且key的长度相等的时候,浅比较也仅仅是用Object.is()对Object的value做了一个基本数据类型的比较,所以如果key里面是对象的话,有可能出现比较不符合预期的情况,所以浅比较是不适用于嵌套类型的比较的。

    相关文章

      网友评论

        本文标题:React Native 性能优化组件-PureComponen

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