React-如何跳过子组件更新?

1,066 阅读3分钟

什么是shouldComponent?

  1. React中的一个生命周期,
  2. 运行时机:在getDerivedStateFromProps之后,render之前执行
  3. 触发条件:
    a. props更新
    b. setState

forceUpdate不会导致shouldComponentUpdate的触发

  1. 作用,如果返回true,那组件就继续render;如果返回false,组件就不更新渲染

什么是pureComponent

  1. React的一种组件类;
  2. 与React.Component很相似。两者的区别在于React.Component中,并没有实现shouldComponentUpdate,需要继承类自己实现。而React.PureComponent中,会浅层对比prop和state,如果内容相同,那么组件将会跳过此次的render更新;
  3. React.PureComponent 中的 shouldComponentUpdate() 将跳过所有子组件树的 prop 更新。因此,请确保所有子组件也都是“纯”的组件。

纯组件的含义,就是传入相同的props对象,总会有相同的渲染内容。

类似于纯函数的定义

4.判断步骤:
如果 PureComponent 里有 shouldComponentUpdate 函数的话,直接使用 shouldComponentUpdate 的结果作为是否更新的依据。

没有 shouldComponentUpdate 函数的话,才会去判断是不是 PureComponent ,是的话再去做 shallowEqual 浅比较。

  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)
    );
  }

从上面的代码就可以看出,pureComponent不是实现了shouldComponentUpdate的Component,而是在对比的时候就返回浅对比的结果。

  1. PureComponent不可滥用,他使用在class组件内,只有那些状态和属性不经常的更新的组件我们用来做优化,对于经常更新的,这样处理后反而浪费性能,因为每一次浅比较也是要消耗时间的

什么是shallowEqual浅比较

这里有写得很不错的文章:浅谈React 中的浅比较是如何工作的

我总结一下这片文章里面写的东西:

  • 浅比较的对象,是新旧两个props、新旧两个state
// PureReactComponent的时候进行浅对比
  if (ctor.prototype && ctor.prototype.isPureReactComponent) {
    return (
      !shallowEqual(oldProps, newProps) || !shallowEqual(oldState, newState)
    );
  }
  • 先判断两个对象是否地址相同。如果地址相同,就直接返回true;如果地址不相同,就继续判断

网上很多文章把第一步的意义判定为,基本类型的相等性判断

  • 再判断有没有不是对象的值,或者等于null的值。如果有,直接返回false;如果没有,就继续判断

只有这一步通过了,下面的判断才有了意义

如果把第一步判定为,基本类型的判断,那第二步又如何解释呢?

话又说回来了,传进来的props或者state一定是对像啊。如果传进来的是非对象,又是怎么做到的呢?

  • 再判断两个props的key数量,是否相同,如果相同就继续下一步的判断;如果不相同,就直接返回false
  • 最后一步,分别判断每个key对应的value值,是否相同。判断value是否相同,使用的是object.is()

此处附上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;
}

什么是React.memo

const MyComponent = React.memo(function MyComponent(props) {
  /* 使用 props 渲染 */
},areEqual);
  1. React.memo是高阶组件,

  2. 包裹其中的组件,并返回新的组件。该组件在props没有变更的时候,就会返回相同的渲染结果,也就是直接跳过渲染阶段。该阶段及其之后的阶段的生命周期函数就不会得到调用。当然,进行的也是浅比较

  3. 用法:

    1. 第一个参数,是函数组件( React.FunctionComponent)
    2. 第二个参数,回调函数。如果我们觉得浅比较不行,我们就填入第二个参数,React会把第二个参数的返回值,当作是否跳过更新的标准
function areEqual(prevProps, nextProps) {
  /*
  如果把 nextProps 传入 render 方法的返回结果与
  将 prevProps 传入 render 方法的返回结果一致则返回 true,
  否则返回 false
  */
}
  1. 与 class 组件中 shouldComponentUpdate() 方法不同的是,如果 props 相等,areEqual 会返回 true;如果 props 不相等,则返回 false。这与 shouldComponentUpdate 方法的返回值相反

这个要引起我们的注意

总结

这是react为跳过子组件更新而做出的努力

  1. 生命周期函数 shouldComponentUpdate
  2. Component类,React.pureComponent
  3. Memo