在react存活期间, 被管控的更新会被优化,异步的更新会产生多次重新渲染。 需要减少render的次数

shouldComponentUpate

该方法在组件实例中用来决定组件是否参与本次更新,会影响后续节点。

返回true会参与更新

  1. // v15
  2. // shouldComponentUpdate 的返回值决定了是否更新
  3. // 在shouldUdate 返回false时, 不会重新渲染而是赋值最新的props和state
  4. {
  5. // If it's determined that a component should not update, we still want
  6. // to set props and state but we shortcut the rest of the update.
  7. this._currentElement = nextParentElement;
  8. this._context = nextUnmaskedContext;
  9. inst.props = nextProps;
  10. inst.state = nextState;
  11. inst.context = nextContext;
  12. }
  13. // 如果更新会对比前后两个组件。 是进入对组件的递归调合还是对组件的卸载重新挂载
  14. function shouldUpdateReactComponent(prevElement, nextElement) {
  15. var prevEmpty = prevElement === null || prevElement === false;
  16. var nextEmpty = nextElement === null || nextElement === false;
  17. if (prevEmpty || nextEmpty) {
  18. return prevEmpty === nextEmpty;
  19. }
  20. var prevType = typeof prevElement;
  21. var nextType = typeof nextElement;
  22. if (prevType === 'string' || prevType === 'number') {
  23. return nextType === 'string' || nextType === 'number';
  24. } else {
  25. return nextType === 'object' &&
  26. prevElement.type === nextElement.type &&
  27. prevElement.key === nextElement.key;
  28. }
  29. }

在v16.8以后如果shouldUpdate 为true,当前workInProgress对effectTag会被打上update 的标记,后续更新。如果为false,对下一次props和state重新赋值为最新的

PureComponent

是react提供的基类, 默认采用优化,间接实现了shouldUpdate方法。 但都是浅比较。如果是基础数据类型还是可以hold住的。 如果是引用类型可以手动实现。但在这里不推荐深层次的比较。可以使用第三方库,不可变数据

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

  if (
    typeof objA !== 'object' || 
    objA === null || 
    typeof objB !== 'object' ||
    objB === null
  ) {
    return false;
  }

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

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

  // Test for A's keys different from B.
  for (var i = 0; i < keysA.length; i++) {
    if (
      !hasOwnProperty$1.call(objB, keysA[i]) || 
      !is(objA[keysA[i]], objB[keysA[i]])
    ) {
      return false;
    }
  }

  return true;
}

memo

memo 为高阶组件,如果函数组件被memo包裹,可以检查props的变更。

如果组件内部使用了hook,比如当context变更时,组件仍然会被穿透更新

第二个参数就是手动用了比较props的,返回false时会参与更新, 与should相反


const Child = memo((props) => {
  return (
    <div>
      {props.count}
    </div>
  )
},
  (prevProps, nextProps) => {
    return prevProps.count === nextProps.count
  }
)
function App() {

  return <Child count={1123} />
}

更新粒度和更新单元

每一次执行渲染的时候,如果没有特别的操作,渲染的最小点会执行到最近的一次变化,也就是说子组件没有变化,而父组件变化了,还是会参与本次更新,所以可以使用上面的两种方式避免这个问题。

如果是两个子组件在同一个容器里,二者的props均来自与此容器,其中一个更新之后,另外一个也会更新

const B = (props) => {
  console.log("b");

  return <div> B,--{props.count} </div>;
};
const A = (props) => {
  console.log("a");
  return <div> A ,,,{props.count}</div>;
};

export default function () {
  const [count1, setCount1] = React.useState(0);
  const [count2, setCount2] = React.useState(0);

  const clickHandler1 = () => setCount1((v) => v + 1);
  const clickHandler2 = () => setCount2((v) => v + 1);

  return (
    <div>
      <button onClick={clickHandler1}>setCount1</button>
      <br />
      <button onClick={clickHandler2}>setCount2</button>
      <B count={count1} />
      <A count={count2} />
    </div>
  );
}

比如👆的A组件,就是count2没有变化,但是仍然会被重新渲染。因为父级组件的变化连带着子组件一起变化。这种情况就可以使用memo包裹一下

const A = React.memo((props) => {
  console.log("a");
  return <div> A ,,,{props.count}</div>;
}, (pp, np) => pp.count === np.count ) ;

此时的A组件,在count2没有变化的时候不会参与更新。此时b组件也会更新

可以把两个组件的状态自行维护,不提升到父级容器,不存在共享就不会变更

拆分到组件按照更新到粒度,如果不存在数据的关联关系。可以避免父组件更新连带的更新。如果多个字组件的状态都放在父组件,每一次的更新都会重新render。 使用shouldUpdate、 useMemo、 useCallback、memo 对类组件和函数组件执行优化,选择性的更新

合理的拆分组件试图与容器的分离可以达到某种优化手段,但优化是多种组合在一起的

使用hooks缓存

对于函数组件,props可以使用hook包裹,在依赖项变更时,hook重新计算,props也会随之变化,反正前后两个props相等不需要更新

比如useMemo和useCallback都是对组件其他的状态变更时才会重新执行create函数。如果没有,就不会更新。

如果传递个子组件的一个回调,回调用来做一些变更操作时不需要依赖其他状态,不需要动态的变化,这个时候可以使用useCallback包裹一下,传递给子组件时,props每次前后都是相同的

key

key值是必须的、唯一的、稳定的

当基于下标的组件重新排序时,组件state就会遇到问题,比如删除和新建与预期不一致

组件实例是基于它们的key来决定是否更新以及复用,如果key是一个下标,那么修改顺序时会修改当前的key,导致非受控组件的state可能相互篡改出现无法预期的变动

key是组件身份的标识,react利用key来识别组件,目的就是找新节点与对应的老节点是否复用还是删除新建