PureRender

shouldComponentUpdate 作用

shouldComponentUpdate 是 PureRender 的关键之所在,这个生命周期钩子函数决定了需不需要执行 render 方法。一个组件的状态更新到真实 dom 的更新大致流程如下:

SCU(shouldComponentUpdate)  --true-->  render  --true-->  vDOMEq  --true--> realDOM

React 官方的例子 为例,补充说明如下:

  • SCU表明了shouldComponentUpdate的返回内容,绿色代表 true,会重新执行 render 方法;红色代表 false,不会执行 render 方法。

  • vDOMEq表明了执行 v-diff 后待渲染的 v-dom 与原始 v-dom 是否相等,绿色代表相等,红色代表不等,需要更新 dom。

  • 圆圈的颜色表明这个组件是否需要更新 dom,红色和 vDOMEq 的值的颜色一致。

由于 C2 的shouldComponentUpdate返回了false,React 不会调用 C2 的 render 方法,也就不会生成 v-dom,甚至不会在 C4 和 C5 上调用shouldComponentUpdate

对 C1 和 C3 来说,shouldComponentUpdate返回了true,因此 React 会深入到分支中并检查它们。C6 的shouldComponentUpdate返回了true,然后进行 v-diff,由于 vDOMEq 返回 false,React 会更新这个真实的 DOM 节点。

最后一个有趣的情况是 C8,SCU 为 true 会调用 render 方法进行 v-diff,但是由于vDOMEq 返回 true,因此它并没有更新这个真实的 DOM 节点。

分析完以上所有过程,我们会发现 React 只需更新 C6 的真实 DOM 节点,因为它是不可避免的。对C8来说,它通过 v-diff 避免了更新真实 DOM,对 C2 的子树和 C7,它们甚至都没有执行比较,因为我们设置了shouldComponentUpdatefalserender没有被调用。

PureRender 本质

PureRender 的本质就是把 shouldComponentUpdate 函数用浅比较(shallowCompare)进行重写。

以下是 React 中 shallowCompare.js 的核心源码:

function shallowCompare(instance, nextProps, nextState) {
  return !shallowEqual(instance.props, nextProps) || !shallowEqual(instance.state, nextState);
}

我们可以看到 shallowCompare 实际返回的是 shallowEqual 的取反结果,那 shallowEqual 的源码则是核心所在,react 的 shallowEqual 其实就是引用的 fibjs 库的 shallowEqual 方法。假设 objA,objB 都是对象,提取关键代码的核心思想进行了改写:

function shallowEqual(objA, objB) {
  if (objA === objB) {
    return true;
  }

  const keysA = Object.keys(objA);
  const keysB = Object.keys(objB);

  if (keysA.length !== keysB.length) {
    return false;
  }

  // 关键代码,只需关注 props 中每一个是否相等,无需深入判断
  return keysA.every(key => 
    Object.hasOwnProperty.call(objB, keys) &&
    objA[key] === objB[key];
  )
}

当 shallowEqual 为 true 的时候,shouldComponentUpdate 为 false,组件不会更新,这样便减少了某些组件的状态更新导致的一些不必要的 render 和 v-diff。

Last updated