mcpField
R
react-performance-optimizer

react-performance-optimizer

github.com/curiositech开发257203 次安装2 天前更新
代码仓库

关于 react-performance-optimizer

react-performance-optimizer 是帮助 React 开发者排查和改善性能问题的技能,提供分析渲染、列表、包体积和内存问题的策略。内容强调先用性能分析工具定位瓶颈,再选择虚拟化、代码拆分或记忆化等方案。

主要功能

  1. 01使用 Profiler 定位慢渲染
  2. 02大型列表虚拟化
  3. 03路由与组件代码拆分
  4. 04谨慎使用记忆化
  5. 05包体积分析与内存问题排查

适用场景

  1. 01诊断组件重复渲染和卡顿
  2. 02优化大量条目的列表滚动
  3. 03缩减首屏加载的 JavaScript 包

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/curiositech/some_claude_skills --skill react-performance-optimizer

安装后在 Claude Code 中输入/react-performance-optimizer确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

.claude-plugin/
plugin.json
references/
bundle-optimization.md
memory-leaks.md
profiling-guide.md
scripts/
bundle_analyzer.sh
performance_audit.ts

详细介绍

React 性能优化器

擅长诊断和修复 React 性能问题,打造流畅如丝的 60fps 体验。

适用场景

✅ 适用于:

  • 组件重新渲染缓慢
  • 大型列表(>100 项)导致卡顿
  • Bundle 体积 >500KB(gzip 压缩后)
  • 可交互时间 >3 秒
  • 滚动或动画卡顿
  • 组件卸载后出现内存泄漏

❌ 不适用于:

  • 组件少于 10 个的应用(过早优化)
  • 后端 API 响应缓慢(修复 API)
  • 网络延迟(使用缓存/CDN)
  • 非 React 框架(使用对应框架的工具)

快速决策树

Is your React app slow?
├── Profiler shows >16ms renders? → Use memoization
├── Lists with >100 items? → Use virtualization
├── Bundle size >500KB? → Code splitting
├── Lighthouse score <70? → Multiple optimizations
└── Feels fast enough? → Don't optimize yet

技术选型

性能工具(2024)
工具用途适用场景
React DevTools Profiler找出缓慢的组件始终从这里开始
Lighthouse整体性能评分前后对比
webpack-bundle-analyzer找出体积较大的依赖项Bundle >500KB
why-did-you-render找出不必要的重新渲染调试重新渲染风暴
React Compiler(2024+)自动记忆化React 19+

时间线:

  • 2018:推出 React.memo、useMemo、useCallback
  • 2020:并发模式(现称并发渲染)
  • 2022:React 18 中的自动批处理
  • 2024:React Compiler(自动优化)
  • 2025+:预计 React Compiler 将取代手动记忆化

常见反模式

反模式 1:过早记忆化

新手想法:“把所有内容都用 useMemo 包起来就能提速”

问题:收益微乎其微,却增加了复杂性和开销。

错误做法:

// ❌ Over-optimization
function UserCard({ user }) {
  const fullName = useMemo(() => `${user.first} ${user.last}`, [user]);
  const age = useMemo(() => new Date().getFullYear() - user.birthYear, [user]);

  return <div>{fullName}, {age}</div>;
}

错误原因:字符串拼接比 useMemo 的开销更低。

正确做法:

// ✅ Simple is fast
function UserCard({ user }) {
  const fullName = `${user.first} ${user.last}`;
  const age = new Date().getFullYear() - user.birthYear;

  return <div>{fullName}, {age}</div>;
}

经验法则:仅在以下情况下使用记忆化:

  1. 计算耗时 >5ms(使用 Profiler 测量)
  2. 结果会在依赖数组中使用
  3. 可以避免子组件重新渲染

反模式 2:不记忆化回调

问题:每次渲染都会创建新的函数实例,导致 React.memo 失效。

错误做法:

// ❌ Child re-renders on every parent render
function Parent() {
  const [count, setCount] = useState(0);

  return (
    <Child onUpdate={() => setCount(count + 1)} />
  );
}

const Child = React.memo(({ onUpdate }) => {
  return <button onClick={onUpdate}>Update</button>;
});

错误原因:箭头函数会创建新的引用 → React.memo 形同虚设。

正确做法:

// ✅ Stable callback reference
function Parent() {
  const [count, setCount] = useState(0);

  const handleUpdate = useCallback(() => {
    setCount(c => c + 1);  // Updater function avoids dependency
  }, []);

  return <Child onUpdate={handleUpdate} />;
}

const Child = React.memo(({ onUpdate }) => {
  return <button onClick={onUpdate}>Update</button>;
});

反模式 3:渲染大型列表时不使用虚拟化

问题:渲染 1000+ 个 DOM 节点会导致卡顿。

症状:滚动卡顿,首次渲染缓慢。

错误做法:

// ❌ Renders all 10,000 items
function UserList({ users }) {
  return (
    <div>
      {users.map(user => (
        <UserCard key={user.id} user={user} />
      ))}
    </div>
  );
}

正确做法:

// ✅ Only renders visible items
import { FixedSizeList } from 'react-window';

function UserList({ users }) {
  return (
    <FixedSizeList
      height={600}
      itemCount={users.length}
      itemSize={50}
      width="100%"
    >
      {({ index, style }) => (
        <div style={style}>
          <UserCard user={users[index]} />
        </div>
      )}
    </FixedSizeList>
  );
}

效果:10,000 个项目:渲染耗时从 5 秒降至 50ms。


反模式 4:不进行代码拆分

问题:一开始就下载 2MB 的 Bundle,导致初始加载缓慢。

错误做法:

// ❌ Everything in main bundle
import AdminPanel from './AdminPanel';  // 500KB
import Dashboard from './Dashboard';
import Settings from './Settings';

function App() {
  return (
    <Routes>
      <Route path="/admin" element={<AdminPanel />} />
      <Route path="/dashboard" element={<Dashboard />} />
      <Route path="/settings" element={<Settings />} />
    </Routes>
  );
}

正确做法:

// ✅ Lazy load routes
import { lazy, Suspense } from 'react';

const AdminPanel = lazy(() => import('./AdminPanel'));
const Dashboard = lazy(() => import('./Dashboard'));
const Settings = lazy(() => import('./Settings'));

function App() {
  return (
    <Suspense fallback={<Loading />}>
      <Routes>
        <Route path="/admin" element={<AdminPanel />} />
        <Route path="/dashboard" element={<Dashboard />} />
        <Route path="/settings" element={<Settings />} />
      </Routes>
    </Suspense>
  );
}

效果:初始 Bundle:从 2MB 降至 300KB。


反模式 5:在渲染过程中执行高开销操作

问题:每次渲染都会执行大量计算。

错误做法:

// ❌ Sorts on every render (even when data unchanged)
function ProductList({ products }) {
  const sorted = products.sort((a, b) => b.price - a.price);

  return <div>{sorted.map(p => <Product product={p} />)}</div>;
}

正确做法:

// ✅ Memoize expensive operation
function ProductList({ products }) {
  const sorted = useMemo(
    () => [...products].sort((a, b) => b.price - a.price),
    [products]
  );

  return <div>{sorted.map(p => <Product product={p} />)}</div>;
}

实现模式

模式 1:使用 React.memo 处理纯组件
// Prevent re-render when props unchanged
const ExpensiveComponent = React.memo(({ data }) => {
  // Complex rendering logic
  return <div>{/* ... */}</div>;
});

// With custom comparison
const UserCard = React.memo(
  ({ user }) => <div>{user.name}</div>,
  (prevProps, nextProps) => {
    // Return true if props equal (skip re-render)
    return prevProps.user.id === nextProps.user.id;
  }
);
模式 2:使用 useMemo 执行高开销计算
function DataTable({ rows, columns }) {
  const sortedAndFiltered = useMemo(() => {
    console.log('Recomputing...');  // Only logs when rows/columns change

    return rows
      .filter(row => row.visible)
      .sort((a, b) => a.timestamp - b.timestamp);
  }, [rows, columns]);

  return <Table data={sortedAndFiltered} />;
}
模式 3:使用 useCallback 保持引用稳定
function SearchBox({ onSearch }) {
  const [query, setQuery] = useState('');

  // Stable reference, doesn't break child memoization
  const handleSubmit = useCallback(() => {
    onSearch(query);
  }, [query, onSearch]);

  return (
    <form onSubmit={handleSubmit}>
      <input value={query} onChange={e => setQuery(e.target.value)} />
    </form>
  );
}
模式 4:虚拟化(react-window)
import { VariableSizeList } from 'react-window';

function MessageList({ messages }) {
  const getItemSize = (index) => {
    // Dynamic heights based on content
    return messages[index].text.length > 100 ? 80 : 50;
  };

  return (
    <VariableSizeList
      height={600}
      itemCount={messages.length}
      itemSize={getItemSize}
      width="100%"
    >
      {({ index, style }) => (
        <div style={style}>
          <Message message={messages[index]} />
        </div>
      )}
    </VariableSizeList>
  );
}
模式 5:使用 React.lazy 进行代码拆分
// Route-based splitting
const routes = [
  { path: '/home', component: lazy(() => import('./Home')) },
  { path: '/about', component: lazy(() => import('./About')) },
  { path: '/contact', component: lazy(() => import('./Contact')) }
];

// Component-based splitting
const HeavyChart = lazy(() => import('./HeavyChart'));

function Dashboard() {
  const [showChart, setShowChart] = useState(false);

  return (
    <div>
      <button onClick={() => setShowChart(true)}>Show Chart</button>

      {showChart && (
        <Suspense fallback={<Spinner />}>
          <HeavyChart />
        </Suspense>
      )}
    </div>
  );
}

生产环境检查清单

□ Profiler analysis completed (identified slow components)
□ Large lists use virtualization (&gt;100 items)
□ Routes code-split with React.lazy
□ Heavy components lazy-loaded
□ Callbacks memoized with useCallback
□ Expensive computations use useMemo
□ Pure components wrapped in React.memo
□ Bundle analyzed (no duplicate dependencies)
□ Tree-shaking enabled (ESM imports)
□ Images optimized and lazy-loaded
□ Lighthouse score &gt;90
□ Time to Interactive &lt;3 seconds

适用与不适用场景

场景是否优化?
渲染 1000+ 个列表项目✅ 是 - 使用虚拟化
对大型数组排序/筛选✅ 是 - 使用 useMemo
向记忆化子组件传递回调✅ 是 - 使用 useCallback
字符串拼接❌ 否 - 已足够快
简单算术运算❌ 否 - 不要记忆化
10 项列表❌ 否 - 过早优化

参考资料

  • /references/profiling-guide.md - React DevTools Profiler 使用方法
  • /references/bundle-optimization.md - 缩减 Bundle 体积的策略
  • /references/memory-leaks.md - 检测和修复内存泄漏

脚本

  • scripts/performance_audit.ts - 自动化性能检查
  • scripts/bundle_analyzer.sh - 分析并可视化打包文件

此技能指南涵盖:React 性能优化 | 记忆化 | 虚拟化 | 代码拆分 | 打包文件优化 | 性能分析

评论

支持 Markdown