performanceoptimization

LearningReactperformanceoptimizationtechniques, includingcomponentoptimization, 渲染optimization, statusmanagementoptimizationetc. in 容

1. Reactperformanceoptimizationoverview

React is a high performance before 端framework, 但 in processing complex application时, 仍然需要forperformanceoptimization. performanceoptimization可以分 for many 个层面, includingcomponentoptimization, 渲染optimization, statusmanagementoptimizationetc..

1.1 performanceoptimization 目标

  • reducing不必要 重 new 渲染
  • optimization渲染速度
  • reducingmemory占用
  • improvingapplicationresponse速度
  • optimizationresource加载

2. componentoptimization

componentoptimization is Reactperformanceoptimization Basics, 可以through many 种方式reducingcomponent 重 new 渲染.

2.1 usingReact.memo

React.memo is a high 阶component, 用于cachecomponent 渲染结果, 只 has 当component props发生变化时才会重 new 渲染.

const MemoizedComponent = React.memo(Component);

// usingexample
const ExpensiveComponent = React.memo(({ data }) => {
  console.log('ExpensiveComponent rendered');
  // 执行昂贵 计算
  return <div>{data}</div>;
});

function ParentComponent() {
  const [count, setCount] = useState(0);
  const [data, setData] = useState('test');
  
  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => setCount(count + 1)}>Increment</button>
      <ExpensiveComponent data={data} />
    </div>
  );
}

提示

React.memo默认using浅比较来比较props, such as果props is complex object, 需要providing自定义比较function.

2.2 usinguseMemo and useCallback

useMemo用于cache计算结果, useCallback用于cachefunction引用, 避免不必要 重 new 计算 and functioncreation.

// useMemoexample
function ExpensiveCalculation() {
  const [count, setCount] = useState(0);
  const [number, setNumber] = useState(10);
  
  const expensiveResult = useMemo(() => {
    console.log('Calculating expensive result...');
    let result = 0;
    for (let i = 0; i < 100000000; i++) {
      result += i;
    }
    return result + number;
  }, [number]);
  
  return (
    <div>
      <p>Count: {count}</p>
      <p>Expensive Result: {expensiveResult}</p>
      <button onClick={() => setCount(count + 1)}>Increment Count</button>
      <button onClick={() => setNumber(number + 1)}>Increment Number</button>
    </div>
  );
}

// useCallbackexample
const MemoizedChild = React.memo(({ onButtonClick }) => {
  console.log('MemoizedChild rendered');
  return <button onClick={onButtonClick}>Click me</button>;
});

function ParentComponent() {
  const [count, setCount] = useState(0);
  
  const handleClick = useCallback(() => {
    console.log('Button clicked');
  }, []);
  
  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => setCount(count + 1)}>Increment</button>
      <MemoizedChild onButtonClick={handleClick} />
    </div>
  );
}

2.3 usingshouldComponentUpdate

for 于classcomponent, 可以usingshouldComponentUpdate生命周期method来控制component is 否需要重 new 渲染.

class CustomComponent extends React.Component {
  shouldComponentUpdate(nextProps, nextState) {
    // 只 has 当props.id or state.count发生变化时才重 new 渲染
    return nextProps.id !== this.props.id || nextState.count !== this.state.count;
  }
  
  render() {
    return <div>{this.props.id} - {this.state.count}</div>;
  }
}

2.4 usingPureComponent

PureComponent is Reactproviding 一个基class, 它implementation了shouldComponentUpdatemethod, using浅比较来比较props and state.

class PureCustomComponent extends React.PureComponent {
  render() {
    return <div>{this.props.data}</div>;
  }
}

3. 渲染optimization

渲染optimization可以reducingDOMoperation, improving渲染速度.

3.1 usingkeyproperty

in 渲染list时, using stable keyproperty可以helpingReact识别哪些元素发生了变化, 避免不必要 DOMoperation.

// error 做法
const list = items.map((item, index) => (
  <Item key={index} data={item} />
));

// 正确 做法
const list = items.map((item) => (
  <Item key={item.id} data={item} />
));

3.2 避免 in 联样式 and in 联function

in 联样式 and in 联function会导致component每次渲染时creation new 引用, 影响React diffalgorithms.

// error 做法
function Component() {
  return (
    <div style={{ color: 'red' }} onClick={() => console.log('clicked')}>
      Click me
    </div>
  );
}

// 正确 做法
const styles = { color: 'red' };

function Component() {
  const handleClick = useCallback(() => {
    console.log('clicked');
  }, []);
  
  return (
    <div style={styles} onClick={handleClick}>
      Click me
    </div>
  );
}

3.3 usingvirtualizationlist

for 于 long list, 可以usingvirtualizationtechniques只渲染可见区域 元素, reducingDOMnode数量.

// usingreact-windowlibrary
import { FixedSizeList as List } from 'react-window';

function VirtualizedList({ items }) {
  const Row = ({ index, style }) => {
    return (
      <div style={style}>
        {items[index]}
      </div>
    );
  };
  
  return (
    <List
      height={400}
      itemCount={items.length}
      itemSize={35}
      width="100%"
    >
      {Row}
    </List>
  );
}

4. statusmanagementoptimization

statusmanagementoptimization可以reducing不必要 statusupdate and 重 new 渲染.

4.1 避免status提升过 high

status提升过 high 会导致更 many component 重 new 渲染, 应该将status尽可能地保留 in 需要它 componentin.

4.2 using不可变data

using不可变data可以helpingReact更 high 效地检测status变化, 避免不必要 重 new 渲染.

// error 做法
state.items.push(newItem);
setState(state);

// 正确 做法
setState(prevState => ({
  ...prevState,
  items: [...prevState.items, newItem]
}));

// usingImmerlibrary
import produce from 'immer';

setState(produce(draft => {
  draft.items.push(newItem);
}));

4.3 批量updatestatus

React会批量processingstatusupdate, 避免 many 次重 new 渲染.

// React会将这两次setStatemerge for 一次update
setState({ count: count + 1 });
setState({ name: 'new name' });

//  in asynchronousfunctionin, 需要usinguseState function形式来确保获取最 new  status
setTimeout(() => {
  setState(prevState => ({ count: prevState.count + 1 }));
  setState(prevState => ({ count: prevState.count + 1 }));
}, 0);

5. resourceoptimization

resourceoptimization可以reducingapplication 加载时间 and run时resource占用.

5.1 code分割

usingcode分割可以将applicationcode拆分 for many 个bundle, 按需加载, reducing初始加载时间.

// usingReact.lazy and Suspense
const LazyComponent = React.lazy(() => import('./LazyComponent'));

function App() {
  return (
    <div>
      <Suspense fallback={<div>Loading...</div>}>
        <LazyComponent />
      </Suspense>
    </div>
  );
}

// usingReact Routerforrouting级别 code分割
const Home = React.lazy(() => import('./Home'));
const About = React.lazy(() => import('./About'));

function App() {
  return (
    <BrowserRouter>
      <Suspense fallback={<div>Loading...</div>}>
        <Routes>
          <Route path="/" element={<Home />} />
          <Route path="/about" element={<About />} />
        </Routes>
      </Suspense>
    </BrowserRouter>
  );
}

5.2 懒加载graph片

懒加载graph片可以reducing初始加载时间, 只 in graph片进入视口时才加载.

// usingloading="lazy"property
<img src="large-image.jpg" loading="lazy" alt="Large image" />

// usingIntersection Observer API
function LazyImage({ src, alt }) {
  const imgRef = useRef(null);
  const [isLoaded, setIsLoaded] = useState(false);
  
  useEffect(() => {
    const observer = new IntersectionObserver(
      (entries) => {
        if (entries[0].isIntersecting) {
          setIsLoaded(true);
          observer.disconnect();
        }
      },
      { threshold: 0.1 }
    );
    
    if (imgRef.current) {
      observer.observe(imgRef.current);
    }
    
    return () => {
      if (imgRef.current) {
        observer.unobserve(imgRef.current);
      }
    };
  }, []);
  
  return (
    <img
      ref={imgRef}
      src={isLoaded ? src : ''}
      alt={alt}
      style={{ minHeight: '200px' }}
    />
  );
}

6. performancemonitor and debug

usingperformancemonitortool可以helping我们识别performance瓶颈, for针 for 性optimization.

6.1 usingReact DevTools

React DevTools is React官方providing debugtool, 可以查看componenttree, checkcomponent props and state, analysiscomponent 渲染performance.

6.2 usingPerformance API

using浏览器 Performance API可以测量component 渲染时间, 识别performance瓶颈.

function PerformanceMonitor({ children }) {
  const start = useRef(null);
  
  useEffect(() => {
    start.current = performance.now();
  }, []);
  
  useEffect(() => {
    if (start.current) {
      const end = performance.now();
      console.log(`Component rendered in ${end - start.current}ms`);
    }
  });
  
  return children;
}

// usingexample
<PerformanceMonitor>
  <ExpensiveComponent />
</PerformanceMonitor>

6.3 usingProfilercomponent

React Profiler is React 16.5引入 performanceanalysistool, 可以测量component 渲染时间 and 次数.

<Profiler id="App" onRender={(id, phase, actualDuration, baseDuration, startTime, submittingTime) => {
  console.log({
    id,
    phase,
    actualDuration,
    baseDuration,
    startTime,
    submittingTime
  });
}}>
  <App />
</Profiler>

7. best practices

  • 优先usingfunctioncomponent and Hooks
  • usingReact.memo, useMemo and useCallbackoptimizationcomponent
  • 避免不必要 statusupdate and 重 new 渲染
  • using不可变data
  • optimization long list, usingvirtualizationtechniques
  • usingcode分割 and 懒加载
  • optimizationgraph片加载
  • 定期monitor and analysisperformance

练习 1: usingReact.memooptimizationcomponent

  1. creation一个昂贵 component, package含 complex 计算
  2. usingReact.memocache该component
  3. verificationcomponent只 in props变化时才重 new 渲染

练习 2: usinguseMemooptimization计算

  1. creation一个package含昂贵计算 component
  2. usinguseMemocache计算结果
  3. verification计算只 in 依赖项变化时才重 new 执行