# React 性能优化

React 性能优化的主要方向有两个:

  • 减少重新 render 的次数。因为在 React 里最重(花时间最长)的一块就是 reconciliation(简单的可以理解为 diff),如果不 render,就不会 reconciliation。
  • 减少重复计算。对于函数式组件来说,每次 render 都会重新从头开始执行函数调用,对于一些计算函数每次都会重新计算。

# 减少rerender

在使用类组件的时候,使用的 React 优化 API 主要是:shouldComponentUpdatePureComponent,这两个 API 所提供的解决思路都是为了减少重新 render 的次数。

函数式组件中,使用React.memo,这个 API 可以说是对标类组件里面的 PureComponent,这是可以减少重新 render 的次数的。

  • React.memo基础用法:通过 React.memo 包裹的组件在 props 不变的情况下,这个被包裹的组件是不会重新渲染的。
    function Component(props) {
        /* 使用 props 渲染 */
    }
    const MyComponent = React.memo(Component);
    
  • React.memo高级用法:默认情况下其只会对 props 的复杂对象做浅比较,可将自定义的比较函数通过第二个参数传入来实现自定义比较。
    function MyComponent(props) {
        /* 使用 props 渲染 */
    }
    function areEqual(prevProps, nextProps) {
        /*
        如果把 nextProps 传入 render 方法的返回结果与
        将 prevProps 传入 render 方法的返回结果一致则返回 true,
        否则返回 false
        */
    }
    exportdefault React.memo(MyComponent, areEqual);
    
    • 注意:React.memo 的第二个参数与类组件中的SCU(shouldComponentUpdate)很相似。不过值得注意的是,它们返回的bool值正好相反(如果 props 相等,areEqual 会返回 true;如果 props 不相等,则返回 false。这与 shouldComponentUpdate 方法的返回值相反)。

如果props中包含类似onClick的callback函数呢

React.memo保证了在父组件传递来的props不变情况下,被包裹组件不会rerender。

但假如props中包含类似onClick的callback函数呢

由于函数式组件每次重新渲染,函数组件都会重头开始执行,也就是说callback函数也会重新生成(即函数引用变了),那么还是会触发React.memo包裹的子组件rerender,这是没必要的。这里就需要用到useCallback了。

  • 使用useCallback,得到一个被记忆的函数,即只在第一次生成,即使函数式组件重选它也不会重新生成:
    const callback = () => {
        doSomething(a, b);
    }
    
    const memoizedCallback = useCallback(callback, [a, b])
    

# 减少重复计算

  • useMemo 与 Vue中的计算属性很像,场景主要是用来缓存计算量比较大的函数结果,可以避免不必要的重复计算。
  • 使用useMemo来做计算结果缓存,即将大计算量的函数通过此hook包裹,缓存一个计算结果:
    function computeExpensiveValue() {
        // 计算量很大的代码
        return xxx
    }
    
    const memoizedValue = useMemo(computeExpensiveValue, [a, b]);
    
  • 注意:如果没有提供依赖项数组deps,useMemo 在每次渲染时都会计算新的值。

# 参考链接

Last Updated: 10/8/2020, 4:07:13 PM