This repository has been archived on 2026-05-24. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
all-in-kingsoft/hhs/REACT/5. 工程实践篇/15-性能优化.md
T
2026-04-29 22:19:51 +08:00

4.5 KiB
Raw Blame History

tags, create time
tags create time
React
Performance
Optimization
Frontend
2026-04-29 22:14

性能优化

概述

React 性能优化的核心原则是 "减少不必要的渲染"。本文档从诊断工具到具体手段,提供完整的调优方法论。

性能诊断工具箱

graph TB
    A[发现性能问题] --> B["选择诊断工具"]
    
    B --> C["React DevTools Profiler"]
    B --> D["Chrome Performance Tab"]
    B --> E["Lighthouse"]
    B --> F["Web Vitals 监控"]
    
    C --> G["定位重渲染的组件和原因"]
    D --> H["分析主线程阻塞时段"]
    E --> I["整体 LCP/FID/CLS 评分"]
    F --> J["生产环境真实用户数据"]
    
    style C fill:#61DAFB,color:#000
    style H fill:#F5A87D,color:#000

React DevTools Profiler 使用要点

  1. 录制期间进行关键交互(点击、输入)
  2. 观察 Commit 颜色 —— 红色越深表示重渲染越多
  3. 展开组件树,关注 "why did this render" 原因
  4. 对比优化前后的 Commit 时间变化

React.memo —— 阻止子组件重渲染

const ExpensiveList = React.memo(({ items }: { items: Item[] }) => {
  // props.items 引用不变时,跳过整个子树的 re-render
  return (
    <ul>
      {items.map(item => <li key={item.id}>{item.name}</li>)}
    </ul>
  );
}, (prevProps, nextProps) => prevProps.items === nextProps.items);  // 自定义比较函数

[!warning] React.memo 的适用边界

  • 只对纯组件有用——相同 props 必须产生相同的输出
  • 父组件每次传新对象/函数作 prop → memo 无效
  • 简单列表(几十项以内)不需要 memo

虚拟列表(Virtual Scrolling)

当列表项超过数百条时,虚拟滚动通过只渲染可视区域内的 DOM 元素来大幅降低内存占用:

// 方案1:tanstack/virtual(推荐)
import { useVirtualizer } from "@tanstack/react-virtual";

function VirtualList({ items }: { items: string[] }) {
  const parentRef = useRef<HTMLDivElement>(null);
  
  const virtualizer = useVirtualizer({
    count: items.length,
    getScrollElement: () => parentRef.current,
    estimateSize: () => 50,  // 预估每项高度
    overscan: 5,            // 视口上下各多渲染 5 项
  });
  
  return (
    <div ref={parentRef} style={{ height: 400, overflow: "auto" }}>
      <div style={{ height: `${virtualizer.getTotalSize()}px`, position: "relative" }}>
        {virtualizer.getVirtualItems().map(virtualRow => (
          <div
            key={virtualRow.index}
            style={{
              position: "absolute",
              top: 0,
              left: 0,
              width: "100%",
              height: `${virtualRow.size}px`,
              transform: `translateY(${virtualRow.start}px)`,
            }}
          >
            {items[virtualRow.index]}
          </div>
        ))}
      </div>
    </div>
  );
}

[!tip] 何时需要虚拟列表?

  • 列表项 > ~50 且每帧渲染耗时可感知
  • 固定高度的项目比可变高度更容易实现
  • React Window / React Virtualized 是老牌的成熟方案

Code Splitting

路由级拆分

// Next.js App Router(内置)
const AdminPage = lazy(() => import("./pages/Admin"));

// vite + React Router
const Settings = lazy(() => import(/* vite: preload */ "./pages/Settings"));

组件级拆分

import dynamic from "next/dynamic";

// 不加载图表库直到真正需要
const Chart = dynamic(() => import("recharts"), { ssr: false });
// 或带 loading fallback
const HeavyEditor = dynamic(() => import("@monaco-editor/react"), {
  loading: () => <p>Loading editor...</p>,
  ssr: false,
});

Bundle 分析与优化

# 安装插件
npm install --save-dev rollup-plugin-visualizer
# 或在 Next.js 中使用 next-bundle-analyzer

# 生成可视化报告
npx run build && npx visualizer

[!tip] Bundle 大小目标

层级 目标大小(gzipped)
首屏 chunk < 150KB
单个 chunk < 300KB
JS Total < 500KB(SPA)/ < 200KB(PWA)

Lighthouse 关键指标调优

指标 含义 优化方向
FCP(First Contentful Paint) 首次内容绘制 减小首屏 HTML/JS 体积
LCP(Largest Contentful Paint) 最大内容绘制 图片懒加载、预加载关键资源
INP(Interaction to Next Paint) 交互响应延迟 useTransition、删除同步 heavy work
CLS(Cumulative Layout Shift) 布局偏移 预留图片宽高、避免字体闪烁

关联笔记