React性能优化

React应用在复杂场景下容易出现渲染性能瓶颈,合理优化能显著提升用户体验。React性能优化手段的核心在于减少不必要的渲染、控制资源加载和合理使用缓存机制

1. 使用 React.memo 避免子组件无意义重渲染

当父组件更新时,即使子组件props未变,也会默认重新渲染。React.memo可缓存组件输出,仅在props变化时重新更新。

示例Demo:

javascript 复制代码
import React, { useState } from "react";

const ExpensiveComponent = React.memo(({ count }) => {
  console.log("ExpensiveComponent 渲染了");
  return <div>计算结果: {count * 100}</div>;
});

function App() {
  const [count, setCount] = useState(0);
  const [toggle, setToggle] = useState(false);

  return (
    <div style={{ padding: "20px", fontFamily: "sans-serif" }}>
      <h2>React.memo 避免子组件重渲染</h2>
      <button onClick={() => setCount((c) => c + 1)}>增加计数</button>
      <button onClick={() => setToggle((t) => !t)}>切换状态</button>
      <p>当前计数: {count}</p>
      <ExpensiveComponent count={count} />
      <p style={{ marginTop: "16px" }}>
        切换状态不会触发 ExpensiveComponent 重渲染
      </p>
    </div>
  );
}

export default App;

附上codesandbox链接:react-memo
关键点:React.memo默认做浅比较,适用于props为基本类型或不变对象的场景。

2. 使用 useMemo 缓存昂贵计算

对复杂计算(如过滤、排序、数学运算)使用useMemo避免每次渲染都重新执行。

示例Demo:

javascript 复制代码
import React, { useState, useMemo } from "react";

function ExpensivePrimeCounter() {
  const [limit, setLimit] = useState(0);
  const [toggle, setToggle] = useState(false);

  // 模拟昂贵计算:计算小于 limit 的质数个数
  const primeCount = useMemo(() => {
    console.log("✅ 重新计算质数个数(未被缓存)");
    let count = 0;
    for (let i = 2; i <= limit; i++) {
      let isPrime = true;
      for (let j = 2; j * j <= i; j++) {
        if (i % j === 0) {
          isPrime = false;
          break;
        }
      }
      if (isPrime) count++;
    }
    return count;
  }, [limit]); // 仅当 limit 变化时重新计算

  return (
    <div
      style={{
        padding: "2rem",
        fontFamily: "sans-serif",
        maxWidth: "500px",
        margin: "0 auto",
      }}
    >
      <h2>质数计数器</h2>
      <p>
        当前上限:<strong>{limit}</strong>
      </p>
      <p>
        小于 {limit} 的质数个数:<strong>{primeCount}</strong>
      </p>
      <button
        onClick={() => setLimit((c) => c + 1)}
        style={{ margin: "0.5rem" }}
      >
        增加上限
      </button>
      <button onClick={() => setToggle((t) => !t)}>切换状态</button>
      <p style={{ fontSize: "0.8rem", color: "#666" }}>
        注意:仅当上限变化时,质数计算才会重新执行 ------ 缓存生效!
      </p>
    </div>
  );
}

export default ExpensivePrimeCounter;

附上codesandbox链接:react-useMemo
注意:不要滥用useMemo,仅对计算成本高的场景使用(如1000+条数据处理)

3. 使用 useCallback 避免回调函数重新创建

回调函数作为props传递给子组件时,每次渲染都会生成新函数,导致React.memo失效。

示例Demo:

javascript 复制代码
import React from "react";

const Button = React.memo(({ onClick, label }) => {
  console.log("Button rendered");
  return <button onClick={onClick}>{label}</button>;
});

export default function Parent() {
  const [count, setCount] = React.useState(0);

  // ❌ 每次渲染都创建新函数 → Button 会重新渲染
  // const handleClick = () => setCount(c => c + 1);

  // ✅ 使用 useCallback 缓存函数引用
  const handleClick = React.useCallback(() => {
    setCount((c) => c + 1);
  }, []);

  return (
    <div>
      <p>Count: {count}</p>
      <Button label="Increment" onClick={handleClick} />
    </div>
  );
}

附上codesandbox链接:react-useCallback

useCallback与React.memo配合使用,是优化组件树的关键组合。

4. 虚拟滚动:渲染大量列表时只渲染可见项

对长列表(如1000+项)使用虚拟滚动,避免DOM节点爆炸。

javascript 复制代码
import React from 'react';
import { FixedSizeList as List } from 'react-window';

const Row = ({ index, style }) => (
  <div style={style}>
    Item {index}
  </div>
);

function LongList({ items }) {
  return (
    <List
      height={400}
      itemCount={items.length}
      itemSize={35}
      width="100%"
    >
      {Row}
    </List>
  );
}

需安装依赖:react-windowreact-virtualized

性能提升:从渲染1000个DOM节点 → 仅渲染10~20个可见节点

5. 懒加载组件:按需加载非关键模块

使用React.lazy + Suspense实现代码分割,减少首屏加载体积。

javascript 复制代码
import React, { lazy, Suspense } from 'react';

const HeavyComponent = lazy(() => import('./HeavyComponent'));

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

适用于:弹窗、配置页、图标组件等非首屏内容

可结合webpack实现自动代码分割,SSR模式下可以使用@loadable/component

6. 使用React DevTools分析渲染性能

安装 React DevTools 浏览器插件,启用"Highlight updates when components render"功能,直观看到哪些组件被不必要地重渲染。

总结

优化手段 适用场景 是否推荐
React.memo 子组件props稳定 强烈推荐
useMemo 复杂计算、昂贵函数 推荐
useCallback 回调传给memoized子组件 推荐
虚拟滚动 长列表(>500项) 必须使用
React.lazy/@loadable/component 非首屏组件 推荐

不要过早优化。先保证功能正确,再用工具定位瓶颈,针对性优化。

相关推荐
I love studying!!!2 小时前
Web应用程序:用户账户
前端·数据库·sqlite
m0_738120722 小时前
渗透知识ctfshow——Web应用安全与防护(三)
android·前端·安全
quxuexi2 小时前
MySQL B+树与复合索引完全指南:从底层原理到高性能优化
b树·mysql·性能优化
下北沢美食家2 小时前
React面试题2
前端·react.js·前端框架
摇滚侠2 小时前
HTML CSS 演示小米 logo 的变化 border-radius 属性设置圆角
前端·css·html
❆VE❆2 小时前
虚拟列表原理与实战运用场景详解
前端·javascript·css·vue.js·html·虚拟列表
leonkay2 小时前
关于.NET中的队列理解
数据库·性能优化·.net·个人开发·设计规范·队列
weixin_408099672 小时前
【实战教程】EasyClick 调用 OCR 文字识别 API(自动识别屏幕文字 + 完整示例代码)
前端·人工智能·后端·ocr·api·安卓·easyclick
CSharp精选营2 小时前
C# 如何减少代码运行时间:7 个实战技巧
性能优化·c#·.net·技术干货·实战技巧