性能优化 #

一、性能优化概述 #

1.1 为什么需要性能优化 #

React 应用可能遇到的性能问题:

问题 表现
不必要渲染 组件频繁重新渲染
大型列表卡顿 滚动不流畅
首屏加载慢 白屏时间长
内存泄漏 页面越来越慢

1.2 性能检测工具 #

javascript
// React DevTools Profiler
// 1. 安装React DevTools浏览器扩展
// 2. 打开Profiler面板
// 3. 点击录制,操作页面
// 4. 分析渲染性能

// 使用React内置Profiler
import { Profiler } from 'react';

function App() {
  const onRender = (id, phase, actualDuration) => {
    console.log(`${id} ${phase} took ${actualDuration}ms`);
  };

  return (
    <Profiler id="App" onRender={onRender}>
      <Component />
    </Profiler>
  );
}

二、避免不必要渲染 #

2.1 React.memo #

javascript
// 默认情况下,父组件更新,子组件也会更新
const Child = React.memo(function Child({ name }) {
  console.log('Child render');
  return <div>{name}</div>;
});

// 自定义比较函数
const Child = React.memo(
  function Child({ user }) {
    return <div>{user.name}</div>;
  },
  (prevProps, nextProps) => {
    return prevProps.user.id === nextProps.user.id;
  }
);

2.2 useMemo #

javascript
function ExpensiveComponent({ list, filter }) {
  // 缓存计算结果
  const filteredList = useMemo(() => {
    console.log('Computing...');
    return list.filter(item => item.includes(filter));
  }, [list, filter]);

  // 缓存复杂对象
  const config = useMemo(() => ({
    options: list.map(item => ({ label: item, value: item })),
    settings: { multi: true }
  }), [list]);

  return <List data={filteredList} config={config} />;
}

2.3 useCallback #

javascript
function Parent() {
  const [count, setCount] = useState(0);
  const [items, setItems] = useState([]);

  // 缓存回调函数
  const handleClick = useCallback(() => {
    console.log('clicked');
  }, []);

  // 带依赖的回调
  const handleAdd = useCallback((item) => {
    setItems(prev => [...prev, item]);
  }, []);

  return (
    <div>
      <button onClick={() => setCount(count + 1)}>{count}</button>
      <Child onClick={handleClick} onAdd={handleAdd} />
    </div>
  );
}

const Child = React.memo(function Child({ onClick, onAdd }) {
  console.log('Child render');
  return (
    <div>
      <button onClick={onClick}>Click</button>
      <button onClick={() => onAdd('new')}>Add</button>
    </div>
  );
});

2.4 虚拟化长列表 #

javascript
import { FixedSizeList } from 'react-window';

function VirtualList({ items }) {
  const Row = ({ index, style }) => (
    <div style={style}>
      {items[index].name}
    </div>
  );

  return (
    <FixedSizeList
      height={400}
      width={300}
      itemCount={items.length}
      itemSize={35}
    >
      {Row}
    </FixedSizeList>
  );
}

三、状态优化 #

3.1 状态下沉 #

javascript
// ❌ 状态在顶层,导致大量组件重新渲染
function App() {
  const [value, setValue] = useState('');
  
  return (
    <div>
      <Header />
      <Main />
      <Footer />
      <input value={value} onChange={(e) => setValue(e.target.value)} />
    </div>
  );
}

// ✅ 状态下沉到需要的组件
function App() {
  return (
    <div>
      <Header />
      <Main />
      <Footer />
      <SearchInput />
    </div>
  );
}

function SearchInput() {
  const [value, setValue] = useState('');
  return <input value={value} onChange={(e) => setValue(e.target.value)} />;
}

3.2 状态提升 #

javascript
// 当多个组件需要共享状态时,提升到共同父组件
function App() {
  const [selected, setSelected] = useState(null);

  return (
    <div>
      <List selected={selected} onSelect={setSelected} />
      <Detail selected={selected} />
    </div>
  );
}

3.3 使用Context避免prop drilling #

javascript
const UserContext = createContext();

function App() {
  const [user, setUser] = useState(null);

  return (
    <UserContext.Provider value={{ user, setUser }}>
      <Layout />
    </UserContext.Provider>
  );
}

function Profile() {
  const { user } = useContext(UserContext);
  return <div>{user?.name}</div>;
}

3.4 状态拆分 #

javascript
// ❌ 单个大状态对象
const [state, setState] = useState({
  user: null,
  posts: [],
  comments: [],
  settings: {}
});

// ✅ 拆分为独立状态
const [user, setUser] = useState(null);
const [posts, setPosts] = useState([]);
const [comments, setComments] = useState([]);
const [settings, setSettings] = useState({});

四、代码分割 #

4.1 动态import #

javascript
import { lazy, Suspense } from 'react';

// 懒加载组件
const Dashboard = lazy(() => import('./Dashboard'));
const Settings = lazy(() => import('./Settings'));

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

4.2 路由级代码分割 #

javascript
import { lazy, Suspense } from 'react';
import { BrowserRouter, Routes, Route } from 'react-router-dom';

const Home = lazy(() => import('./pages/Home'));
const About = lazy(() => import('./pages/About'));
const Dashboard = lazy(() => import('./pages/Dashboard'));

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

4.3 条件加载 #

javascript
function App() {
  const [showDashboard, setShowDashboard] = useState(false);

  const loadDashboard = async () => {
    const module = await import('./Dashboard');
    setDashboard(() => module.default);
    setShowDashboard(true);
  };

  return (
    <div>
      <button onClick={loadDashboard}>Load Dashboard</button>
      {showDashboard && <Dashboard />}
    </div>
  );
}

五、列表优化 #

5.1 使用key #

javascript
// ❌ 使用index作为key
{items.map((item, index) => <Item key={index} item={item} />)}

// ✅ 使用唯一ID作为key
{items.map(item => <Item key={item.id} item={item} />)}

5.2 避免内联函数 #

javascript
// ❌ 每次渲染创建新函数
{items.map(item => (
  <Item
    key={item.id}
    onClick={() => handleClick(item.id)}
  />
))}

// ✅ 使用useCallback或传递item
const handleItemClick = useCallback((id) => {
  console.log(id);
}, []);

{items.map(item => (
  <Item
    key={item.id}
    id={item.id}
    onClick={handleItemClick}
  />
))}

5.3 分页/虚拟滚动 #

javascript
// 分页
function PaginatedList({ items, pageSize }) {
  const [page, setPage] = useState(1);
  
  const paginatedItems = items.slice(
    (page - 1) * pageSize,
    page * pageSize
  );

  return (
    <div>
      {paginatedItems.map(item => (
        <Item key={item.id} item={item} />
      ))}
      <Pagination
        current={page}
        total={Math.ceil(items.length / pageSize)}
        onChange={setPage}
      />
    </div>
  );
}

六、图片优化 #

6.1 懒加载图片 #

javascript
function LazyImage({ src, alt }) {
  const [loaded, setLoaded] = useState(false);
  const [inView, setInView] = useState(false);
  const imgRef = useRef();

  useEffect(() => {
    const observer = new IntersectionObserver(([entry]) => {
      if (entry.isIntersecting) {
        setInView(true);
        observer.disconnect();
      }
    });

    if (imgRef.current) {
      observer.observe(imgRef.current);
    }

    return () => observer.disconnect();
  }, []);

  return (
    <div ref={imgRef}>
      {inView ? (
        <img
          src={src}
          alt={alt}
          onLoad={() => setLoaded(true)}
          style={{ opacity: loaded ? 1 : 0 }}
        />
      ) : (
        <div className="placeholder" />
      )}
    </div>
  );
}

6.2 响应式图片 #

javascript
function ResponsiveImage({ src, alt, sizes }) {
  return (
    <picture>
      <source
        media="(min-width: 1200px)"
        srcSet={`${src}?w=1200 1x, ${src}?w=2400 2x`}
      />
      <source
        media="(min-width: 768px)"
        srcSet={`${src}?w=768 1x, ${src}?w=1536 2x`}
      />
      <img
        src={`${src}?w=400`}
        srcSet={`${src}?w=400 1x, ${src}?w=800 2x`}
        alt={alt}
        sizes={sizes}
        loading="lazy"
      />
    </picture>
  );
}

七、网络优化 #

7.1 数据缓存 #

javascript
const cache = new Map();

function useFetch(url) {
  const [data, setData] = useState(() => cache.get(url));

  useEffect(() => {
    if (cache.has(url)) {
      setData(cache.get(url));
      return;
    }

    fetch(url)
      .then(res => res.json())
      .then(data => {
        cache.set(url, data);
        setData(data);
      });
  }, [url]);

  return data;
}

7.2 请求去重 #

javascript
const pendingRequests = new Map();

async function fetchWithDedup(url) {
  if (pendingRequests.has(url)) {
    return pendingRequests.get(url);
  }

  const promise = fetch(url).then(res => res.json());
  pendingRequests.set(url, promise);

  try {
    const data = await promise;
    return data;
  } finally {
    pendingRequests.delete(url);
  }
}

7.3 预加载 #

javascript
function preloadImage(src) {
  return new Promise((resolve) => {
    const img = new Image();
    img.onload = resolve;
    img.src = src;
  });
}

function usePreloadImages(images) {
  useEffect(() => {
    images.forEach(preloadImage);
  }, [images]);
}

八、内存优化 #

8.1 清理副作用 #

javascript
function Component() {
  useEffect(() => {
    const timer = setInterval(() => {
      console.log('tick');
    }, 1000);

    const subscription = eventEmitter.subscribe(handler);

    return () => {
      clearInterval(timer);
      subscription.unsubscribe();
    };
  }, []);
}

8.2 避免内存泄漏 #

javascript
function Component() {
  const [data, setData] = useState(null);

  useEffect(() => {
    let isMounted = true;

    fetchData().then(result => {
      if (isMounted) {
        setData(result);
      }
    });

    return () => {
      isMounted = false;
    };
  }, []);
}

8.3 使用WeakMap缓存 #

javascript
const cache = new WeakMap();

function getCachedValue(obj) {
  if (cache.has(obj)) {
    return cache.get(obj);
  }
  
  const value = computeValue(obj);
  cache.set(obj, value);
  return value;
}

九、性能优化清单 #

9.1 渲染优化 #

优化项 方法
避免不必要渲染 React.memo
缓存计算 useMemo
缓存函数 useCallback
虚拟列表 react-window

9.2 加载优化 #

优化项 方法
代码分割 lazy + Suspense
图片懒加载 IntersectionObserver
预加载 preload/prefetch

9.3 状态优化 #

优化项 方法
状态下沉 状态放在最近组件
状态拆分 避免大状态对象
Context优化 拆分Context

十、总结 #

类别 优化方法
渲染 memo、useMemo、useCallback
列表 key、虚拟滚动、分页
加载 代码分割、懒加载
网络 缓存、去重、预加载
内存 清理副作用、避免泄漏

核心原则:

  • 避免不必要的渲染
  • 合理使用缓存
  • 代码分割按需加载
  • 及时清理资源
最后更新:2026-03-26