React 组件的常见性能优化手段

169 阅读2分钟

复用组件

在协调阶段,组件复用的前提是必须同时满足三个条件:同一层级下、同一类型、同一个key值。所以我们要尽量保证这三者的稳定性

image.png

常见错误:key=Math.random() 常见不规范写法:key=index

减少组件的render

组件重新render会导致组件进入协调,协调的核心就是常说的vdom diff,所以协调本身就是比较耗时的算法,因此如果能减少协调,复用旧的fiber节点,那么肯定会加快渲染完成的速度。组件如果没有进入协调阶段,我们称为进入bailout阶段,意思就是组件退出更新。

让组件进入bailout阶段,有以下方法

  1. shouldComponentUpdate: component类组件的一个生命周期,当用户定义这个函数并且返回fasle,则进入bailout阶段。
  2. PureComponent: react提供的封装 shouldComponentUpdate的类组件,更新前会自行浅比较新旧props与state是否改变,如果两者都没变,则进入bailout阶段。
  3. memo: 第二个参数可作为用户自定义,如果没有自定义则使用浅比较,比较组件更新前后的props是否相同,如果相同,则进入bailout阶段。
  const ChildMemo = memo(
    function Child({item}) {
      console.log('memo funtion')
      return <div>memo</div>
    }
    // 这里的arePropsEqual 是个函数
    // , (pre, next) => {
    //   return pre.item === next.item
    // } 
  )

附上源码部分 image.png

  1. useMemo 可以缓存参数,可以对比useCallback使用,useCallback(fn, deps) 相当于useMemo(() => fn, deps)
const cachedValue = useMemo(calculateValue, dependencies)

const cachedFn = useCallback(fn, dependencies)
  1. Context:Context本身就是通过Provider传递的value变化,所有消费这个value的后代组件都要更新,因此应该精简使用Context。

如下面的例子,点击任何一个button,都会导致 UserName 和 UserAge重新渲染,这不太合理,因为 UserAgeappContextValue.name无关,UserNameappContextValue.age无关。

import React, { useState } from "react";

export default function App() {
  const [appContextValue, setContextValue] = useState({
    name: 'zzzzz',
    age: 23
  });

  return (
    <div>
      <button
        onClick={() => {
          setContextValue({
            ...appContextValue,
            age: appContextValue.age++,
          });
        }}
      ></button>

      <button
        onClick={() => {
          setContextValue({
            ...appContextValue,
            name: appContextValue.name + `age: ${appContextValue.age}`,
          });
        }}
      ></button>

      <AppContext.Provider value={appContextValue}>
        <UserName />
        <UserAge />
      </AppContext.Provider>
    </div>
  );
}