mcpskills.net
技能MCP智能体提示词
mcpskills.net — A curated directory of AI agent Skills and MCP servers
TermsPrivacy
← 返回技能
Development

性能优化

优化应用性能。在存在性能要求、怀疑出现性能回退,或需要改进 Core Web Vitals 时使用。

作者:Addy Osmani仓库 →来源 →

先度量,再优化。没有度量的性能工作只是凭空猜测。

使用场景

  • 规范中存在性能要求
  • 用户或监控反映出现了缓慢行为
  • Core Web Vitals 分数低于阈值
  • 你怀疑某次变更引入了性能回退
  • 构建处理大数据集或高流量的功能

Core Web Vitals 目标

| 指标 | 良好(Good) | 需改进(Needs Improvement) | 差(Poor) | |--------|------|-------------------|------| | LCP(最大内容绘制) | ≤ 2.5s | ≤ 4.0s | > 4.0s | | INP(下一次绘制交互) | ≤ 200ms | ≤ 500ms | > 500ms | | CLS(累积布局偏移) | ≤ 0.1 | ≤ 0.25 | > 0.25 |

优化工作流

1. MEASURE  → Establish baseline with real data
2. IDENTIFY → Find the actual bottleneck (not assumed)
3. FIX      → Address the specific bottleneck
4. VERIFY   → Measure again, confirm improvement
5. GUARD    → Add monitoring or tests to prevent regression

第 1 步:度量

两种互补的方法 —— 两者都要用:

  • 合成监测(Lighthouse、DevTools): 受控条件,可复现
  • 真实用户监测 RUM(web-vitals 库、CrUX): 真实条件下的真实用户数据

前端:

import { onLCP, onINP, onCLS } from 'web-vitals';

onLCP(console.log);
onINP(console.log);
onCLS(console.log);

后端:

console.time('db-query');
const result = await db.query(...);
console.timeEnd('db-query');

第 2 步:定位瓶颈

前端:

| 症状 | 可能原因 | 排查方式 | |---------|-------------|---------------| | LCP 缓慢 | 大图片、阻塞渲染的资源 | 检查网络瀑布图、图片尺寸 | | CLS 偏高 | 未指定尺寸的图片、延迟加载的内容 | 检查布局偏移归因 | | INP 较差 | 主线程上有繁重的 JavaScript | 在性能跟踪中检查长任务 |

后端:

| 症状 | 可能原因 | 排查方式 | |---------|-------------|---------------| | API 响应缓慢 | N+1 查询、缺少索引 | 检查数据库查询日志 | | 内存增长 | 引用泄漏、无界缓存 | 堆快照分析 | | CPU 飙升 | 同步的繁重计算 | CPU 性能剖析 |

第 3 步:修复常见反模式

N+1 查询(后端)

// BAD: N+1 — one query per task for the owner
const tasks = await db.tasks.findMany();
for (const task of tasks) {
  task.owner = await db.users.findUnique({ where: { id: task.ownerId } });
}

// GOOD: Single query with join/include
const tasks = await db.tasks.findMany({
  include: { owner: true },
});

无界数据获取

// BAD: Fetching all records
const allTasks = await db.tasks.findMany();

// GOOD: Paginated with limits
const tasks = await db.tasks.findMany({
  take: 20,
  skip: (page - 1) * 20,
  orderBy: { createdAt: 'desc' },
});

不必要的重渲染(React)

// BAD: Creates new object on every render
function TaskList() {
  return <TaskFilters options={{ sortBy: 'date', order: 'desc' }} />;
}

// GOOD: Stable reference
const DEFAULT_OPTIONS = { sortBy: 'date', order: 'desc' } as const;
function TaskList() {
  return <TaskFilters options={DEFAULT_OPTIONS} />;
}

// Use React.memo for expensive components
const TaskItem = React.memo(function TaskItem({ task }: Props) {
  return <div>{/* expensive render */}</div>;
});

// Use useMemo for expensive computations
function TaskStats({ tasks }: Props) {
  const stats = useMemo(() => calculateStats(tasks), [tasks]);
  return <div>{stats.completed} / {stats.total}</div>;
}

包体积过大

// Dynamic import for heavy, rarely-used features
const ChartLibrary = lazy(() => import('./ChartLibrary'));

// Route-level code splitting
const SettingsPage = lazy(() => import('./pages/Settings'));

function App() {
  return (
    <Suspense fallback={<Spinner />}>
      <SettingsPage />
    </Suspense>
  );
}

缺少缓存(后端)

// Cache frequently-read, rarely-changed data
const CACHE_TTL = 5 * 60 * 1000; // 5 minutes
let cachedConfig: AppConfig | null = null;
let cacheExpiry = 0;

async function getAppConfig(): Promise<AppConfig> {
  if (cachedConfig && Date.now() < cacheExpiry) {
    return cachedConfig;
  }
  cachedConfig = await db.config.findFirst();
  cacheExpiry = Date.now() + CACHE_TTL;
  return cachedConfig;
}

性能预算

设定预算并强制执行:

JavaScript bundle: < 200KB gzipped (initial load)
CSS: < 50KB gzipped
Images: < 200KB per image (above the fold)
Fonts: < 100KB total
API response time: < 200ms (p95)
Time to Interactive: < 3.5s on 4G
Lighthouse Performance score: ≥ 90

验证

在任何与性能相关的变更之后:

  • [ ] 存在变更前后的度量数据(具体数字)
  • [ ] 已定位并处理了具体瓶颈
  • [ ] Core Web Vitals 处于「Good」阈值内
  • [ ] 包体积未显著增加
  • [ ] 新的数据获取代码中无 N+1 查询
  • [ ] 性能预算在 CI 中通过(如已配置)
  • [ ] 现有测试仍然通过