React源码解读 | React.PureComponent原理篇

1,583 阅读3分钟

前言

PureComponent通过浅比较props和state来防止页面不必要的渲染,本文是原理篇,通过React源码,解读PureComponent中浅比较规则的具体实现

版本

  • react 16.6.1

结论

  • render中不建议声明对象主要是为了激活PureComponent的渲染过滤规则,防止不必要的组件渲染
  • PureComponent渲染过滤规则为:先校验state和props中的属性数量是否改变,然后用Object.is比较state和props中的属性值是否改变(用Object.is进行比较)
  • 如果state和props中的属性值是引用地址,每次想要触发页面渲染需要保证引用地址更新

源码解读

  1. 根据项目中的ReactBaseClasses.js可知,与component类比较,PureComponent组件多了isPureReactComponent属性
/**
 * 以下代码摘取自“/packages/react/src/ReactBaseClasses.js,139-143行”
 */
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;
  1. 在项目中全局搜索isPureReactComponent属性可查看其作用是用于比较props和state,此处全局搜索后在ReactShallowRenderer.js中找到实现,此处意为,如果props浅比较不相等或者state浅比较不相等,则更新渲染
/**
 * 以下代码摘取自“/packages/react-test-renderer/src/ReactShallowRenderer.js,686-689行”
 */ 
else if (type.prototype && type.prototype.isPureReactComponent) {
  shouldUpdate = !shallowEqual(oldProps, props) || !shallowEqual(oldState, state);
}
  1. 跳转到shallowEqual函数的实现
/**
 * 以下代码摘取自“/packages/shared/shallowEqual.js,全部”
 */
import is from './objectIs';
const hasOwnProperty = Object.prototype.hasOwnProperty;
/**
 * Performs equality by iterating through keys on an object and returning false
 * when any key has values which are not strictly equal between the arguments.
 * Returns true when the values of all keys are strictly equal.
 *
 * 通过遍历对象上的键来执行相等性,
 * 当任意键的值在参数之间不严格相等时返回false,
 * 当所有键的值严格相等时,返回true。
 */
function shallowEqual(objA: mixed, objB: mixed): boolean {
  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;
  }
  // Test for A's keys different from B.
  for (let i = 0; i < keysA.length; i++) {
    if (
      !hasOwnProperty.call(objB, keysA[i]) ||
      !is(objA[keysA[i]], objB[keysA[i]])
    ) {
      return false;
    }
  }
  return true;
}
  1. 由以上代码可知,react浅比较(shallowEqual)规则为
    • 两个参数用is(方法见注释)方法判断是否相等 相等继续执行 不相等返回false
    • 校验是否为对象 是对象继续执行 不是对象返回false
    • 校验对象属性个数是否相等 相等继续执行,不相等返回false
    • 逐个遍历对象的属性,并用is校验是两参数中相对应的对象属性是否相等
    • 下面为算法流程图:

注释

  • 文中项目,指react源码项目
  • 文中截取的代码只截取相关部分,不考虑语意和代码上下文
  • is方法即为Objece.is方法,判断规则如下:
  • 都是 undefined
  • 都是 null
  • 都是 true 或 false
  • 都是相同长度的字符串且相同字符按相同顺序排列
  • 都是相同对象(意味着每个对象有同一个引用)
  • 都是数字且
    • 都是 +0
    • 都是 -0
    • 都是 NaN
    • 或都是非零而且非 NaN 且为同一个值