美文网首页
React PureComponent 学习及浅比较详解

React PureComponent 学习及浅比较详解

作者: 夏天_5de1 | 来源:发表于2019-02-27 18:20 被阅读0次

    为什么用 PureComponent

    PureComponent 是优化 React 应用程序最重要的方法之一,易于实施,只要把继承类从 Component 换成 PureComponent 即可,可以减少不必要的 render 操作的次数,从而提高性能,而且可以少写 shouldComponentUpdate 函数,节省了点代码。

    原理

    当组件更新时,如果组件的 propsstate 都没发生改变, render 方法就不会触发,省去 Virtual DOM 的生成和比对过程,达到提升性能的目的。

    具体就是由于PureComponentshouldeComponentUpdate里,实际是对props/state进行了一个

    浅对比,所以对于嵌套的对象不适用,没办法比较出来。

    浅对比??

    首先让我们来看下 PureComponent 的具体实现

    React里,shouldComponentUpdate 源码为:

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

    那我们再来看看 shallowEqual 的源码到底是神马玩意?

    
    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()

    1. Object.is() 这个函数是用来比较两个值是否相等。但这并不同于 === 或者 ==
    
    2. '==' 比较,会把 undefined, null, '', 0 直接转换成布尔型false
    
        null == undefined // true
    
    3. '===' 它不会进行类型转换,也就是说如果两个值一样,必须符合类型也一样。但是,它还是有两种疏漏的情况。
    
        +0 === -0 // true,但我们期待它返回false
    
        NaN === NaN // false,我们期待它返回true
    

    正因为这些原因,Object.is() 应运而生

    
    // 实际上是Object.is()的polyfill
    
    function(x, y) {
    
        // SameValue algorithm
    
        if (x === y) {
    
        // 处理为+0 != -0的情况
    
          return x !== 0 || 1 / x === 1 / y;
    
        } else {
    
        // 处理 NaN === NaN的情况
    
          return x !== x && y !== y;
    
        }
    
    };
    
    

    了解这个我们再来看看刚才的 shallowEqual 代码

    
    // 用原型链的方法
    
    const hasOwn = Object.prototype.hasOwnProperty
    
    // 这个函数实际上是Object.is()的polyfill
    
    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) {
    
      // 首先对基本数据类型的比较
    
      // !! 若是同引用便会返回 true
    
      if (is(objA, objB)) return true
    
      // 由于Obejct.is()可以对基本数据类型做一个精确的比较, 所以如果不等
    
      // 只有一种情况是误判的,那就是object,所以在判断两个对象都不是object
    
      // 之后,就可以返回false了
    
      if (typeof objA !== 'object' || objA === null ||
    
          typeof objB !== '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
    
    }
    
    

    总结: shallowEqual 会比较 Object.keys(state | props) 的长度是否一致,每一个 key 是否两者都有,并且是否是 一个引用,也就是只比较了 第一层 的值,确实很浅,所以深层的嵌套数据是对比不出来的。

    案例 (易变数据不能使用一个引用)

    
    import React, { PureComponent } from 'react'
    
    class App extends PureComponent {
    
          state = {
    
            items: [1, 2, 3]
    
          }
    
          handleClick = () => {
    
            const { items } = this.state
    
            items.pop()
    
            this.setState({ items })
    
          }
    
          render () {
    
            return (<div>
    
              <ul>
    
                {this.state.items.map(i => <li key={i}>{i}</li>)}
    
              </ul>
    
              <button onClick={this.handleClick}>delete</button>
    
            </div>)
    
          }
    
        }
    
    

    通过这个案例就会发现无论怎么点 delete按钮 li 的数量都不会改变。就是因为 items 用的是一个引用, shallowEqual 的结果为 true 。改正:

    
    handleClick = () => {
    
      const { items } = this.state;
    
      items.pop();
    
      this.setState({ items: [].concat(items) });
    
    }
    
    

    菜鸟有话说。。

    以上及是刚学习React的新手对于PureComponent的浅显认识。阅读并借鉴了几位大神的文章,第一次分享与广大初学者共同交流进步。努力!奋斗!💪💪

    相关文章

      网友评论

          本文标题:React PureComponent 学习及浅比较详解

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