最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • React中Props的浅对比

    正文概述 掘金(ZHANGYU)   2021-05-01   530

    上一周去面试的时候,面试官我PureComponent里是如何对比props的,概念已经牢记脑中,脱口而出就是浅对比,接着面试官问我是如何浅对比的,结果我就没回答上来。

    趁着周末,再来看看源码里是如何实现的。

    类组件的Props对比

    类组件是否需要更新需要实现shouldComponentUpdate方法,通常讲的是如果继承的是PureComponent则会有一个默认浅对比的实现。

    // ReactBaseClasses.js
    function ComponentDummy() {}
    ComponentDummy.prototype = Component.prototype;
    
    /**
     * Convenience component with default shallow equality check for sCU.
     */
    function PureComponent(props, context, updater) {
      this.props = props;
      this.context = context;
      // If a component has string refs, we will assign a different object later.
      this.refs = emptyObject;
      this.updater = updater || ReactNoopUpdateQueue;
    }
    
    const pureComponentPrototype = (PureComponent.prototype = new ComponentDummy());
    pureComponentPrototype.constructor = PureComponent;
    // Avoid an extra prototype jump for these methods.
    Object.assign(pureComponentPrototype, Component.prototype);
    pureComponentPrototype.isPureReactComponent = true;
    

    PureComponent的实现如上,我以前以为在声明时默认会实现shouldComponentUpdate方法,但实际上并没有一个默认的方法。

    接下来看看shouldComponentUpdate方法的调用。

    // ReactFiberClassComponent.js
    function checkShouldComponentUpdate(
      workInProgress,
      ctor,
      oldProps,
      newProps,
      oldState,
      newState,
      nextContext,
    ) {
      const instance = workInProgress.stateNode;
      // 如果实利实现了shouldComponentUpdate则返回调用它的结果
      if (typeof instance.shouldComponentUpdate === 'function') {
        const shouldUpdate = instance.shouldComponentUpdate(
          newProps,
          newState,
          nextContext,
        );
        return shouldUpdate;
      }
    
      // PureReactComponent的时候进行浅对比
      if (ctor.prototype && ctor.prototype.isPureReactComponent) {
        return (
          !shallowEqual(oldProps, newProps) || !shallowEqual(oldState, newState)
        );
      }
    
      return true;
    }
    

    可以看出实际上并没有单独写一个shouldComponentUpdate方法给PureReactComponent,而是在对比的时候就返回浅对比的结果。

    浅对比的答案都在shallowEqual方法里了。

    shallowEqual 浅对比

    // shallowEqual.js
    function shallowEqual(objA: mixed, objB: mixed): boolean {
      // 一样的对象返回true
      if (Object.is(objA, objB)) {
        return true;
      }
    
      // 不是对象或者为null返回false
      if (
        typeof objA !== 'object' ||
        objA === null ||
        typeof objB !== 'object' ||
        objB === null
      ) {
        return false;
      }
    
      const keysA = Object.keys(objA);
      const keysB = Object.keys(objB);
    
      // key数量不同返回false
      if (keysA.length !== keysB.length) {
        return false;
      }
    
      // 对应key的值不相同返回false
      for (let i = 0; i < keysA.length; i++) {
        if (
          !hasOwnProperty.call(objB, keysA[i]) ||
          !Object.is(objA[keysA[i]], objB[keysA[i]])
        ) {
          return false;
        }
      }
    
      return true;
    }
    

    shallowEqual方法原理很简单了

    1. 先判断两者是否为同一对象。
    2. 判断两者的值是否不为object或为null。
    3. 对比两者key的长度。
    4. 判断两者key对应的值是否相同。

    原来原理是这样简单的对比,如果我面试的时候能够口喷源码,会不会工资更高一些呢?

    函数组件的浅对比

    函数组件的浅对比方式则使用React.memo方法实现。

    // ReactMemo.js
    export function memo<Props>(
      type: React$ElementType,
      compare?: (oldProps: Props, newProps: Props) => boolean,
    ) {
      const elementType = {
        $$typeof: REACT_MEMO_TYPE,
        type,
        compare: compare === undefined ? null : compare,
      };
      return elementType;
    }
    

    React.memo方法同样支持传入compare函数最为第二个参数。

    内部的处理其实是手动创建了一个$$typeofREACT_MEMO_TYPEReactElement,方便之后的类型判断。

    React.memo组件的创建会稍微复杂一些,由于可以传入第二个自定义的compare函数,所以在内部其实会被定义为2种类型的Fiber节点。

    • 没有传入compare函数的为SimpleMemoComponent
    • 传入了自定义compare函数的为MemoComponent

    但是实际对于Props的比较都是相同的,默认都是调用shallowEqual方法来对比。

    updateSimpleMemoComponent

    if (
      shallowEqual(prevProps, nextProps) &&
      current.ref === workInProgress.ref
    ) {
    	// ...
    }
    

    updateMemoComponent

    // ...
    let compare = Component.compare;
    compare = compare !== null ? compare : shallowEqual;
    if (compare(prevProps, nextProps) && current.ref === workInProgress.ref) {
      return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes);
    }
    // ... 
    

    至于为什么要分为2个组件,我也没大看懂,蓝廋香菇,大概是和更新调度相关的。

    SimpleMemoComponent的Fiber节点实际等于改了个名的函数组件,走流程会直接走到函数组件里,而MemoComponent则是套了一层壳,需要先把壳剥开生成子Fiber节点,再由子Fiber节点的判断走到函数组件里。


    以上就是Props浅对比的分析了~


    起源地下载网 » React中Props的浅对比

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元