• 设为首页
  • 点击收藏
  • 手机版
    手机扫一扫访问
    迪恩网络手机版
  • 关注官方公众号
    微信扫一扫关注
    公众号

详解React中Props的浅对比

原作者: [db:作者] 来自: [db:来源] 收藏 邀请

上一周去面试的时候,面试官我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函数最为第二个参数。

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

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的浅对比的详细内容,更多关于React中Props的浅对比的资料请关注极客世界其它相关文章!


鲜花

握手

雷人

路过

鸡蛋
该文章已有0人参与评论

请发表评论

全部评论

专题导读
上一篇:
JavaScript实现时间范围效果发布时间:2022-02-05
下一篇:
React合成事件详解发布时间:2022-02-05
热门推荐
热门话题
阅读排行榜

扫描微信二维码

查看手机版网站

随时了解更新最新资讯

139-2527-9053

在线客服(服务时间 9:00~18:00)

在线QQ客服
地址:深圳市南山区西丽大学城创智工业园
电邮:jeky_zhao#qq.com
移动电话:139-2527-9053

Powered by 互联科技 X3.4© 2001-2213 极客世界.|Sitemap