---
name: performance-optimization
description: 优化应用程序性能。当存在性能需求、怀疑性能回归,或需要改进 Core Web Vitals 及加载时间时使用。当性能分析揭示出需要修复的瓶颈时使用。
---
# 性能优化
## 概述
先测量,再优化。不经过测量的性能工作等于猜测——而猜测会导致过早优化,增加复杂度却没有改善真正重要的指标。先做性能分析,找到真正的瓶颈,修复它,再次测量。只优化测量证明确实重要的部分。
## 何时使用
- 规格说明中存在性能要求(加载时间预算、响应时间 SLA)
- 用户或监控报告行为缓慢
- Core Web Vitals 分数低于阈值
- 你怀疑某个变更引入了性能退化
- 构建处理大数据集或高流量的功能时
**何时不应使用:** 在获得问题证据之前不要优化。过早优化会增加复杂度,其成本超过带来的性能收益。
## Core Web Vitals 目标值
| 指标 | 良好 | 待改善 | 差 |
|--------|------|-------------------|------|
| **LCP**(最大内容绘制) | ≤ 2.5s | ≤ 4.0s | > 4.0s |
| **INP**(下次绘制的交互延迟) | ≤ 200ms | ≤ 500ms | > 500ms |
| **CLS**(累积布局偏移) | ≤ 0.1 | ≤ 0.25 | > 0.25 |
## 优化工作流程
```
1. 测量 → 使用真实数据建立基线
2. 识别 → 找到真正的瓶颈(而非假设的瓶颈)
3. 修复 → 针对特定瓶颈进行修复
4. 验证 → 再次测量,确认改善效果
5. 防护 → 添加监控或测试以防止退化
```
### 步骤 1:测量
两种互补的方式——两者都使用:
- **合成测试(Lighthouse、DevTools Performance 面板):** 受控环境,可复现。最适合 CI 回归检测和隔离特定问题。
- **真实用户监控(web-vitals 库、CrUX):** 真实条件下的真实用户数据。需要用它来验证修复是否确实改善了用户体验。
**前端:**
```bash
# 合成测试:Chrome DevTools 中的 Lighthouse(或 CI)
# Chrome DevTools → Performance 面板 → 录制
# Chrome DevTools MCP → 性能追踪
# 真实用户监控:代码中使用 Web Vitals 库
import { onLCP, onINP, onCLS } from 'web-vitals';
onLCP(console.log);
onINP(console.log);
onCLS(console.log);
```
**后端:**
```bash
# 响应时间日志记录
# 应用性能监控(APM)
# 带计时信息的数据库查询日志
# 简单计时
console.time('db-query');
const result = await db.query(...);
console.timeEnd('db-query');
```
### 从何处开始测量
根据症状决定优先测量的内容:
```
什么变慢了?
├── 首次页面加载
│ ├── 打包文件过大? --> 测量打包体积,检查代码分割
│ ├── 服务器响应慢? --> 在 DevTools Network 瀑布图中测量 TTFB
│ │ ├── DNS 耗时长? --> 为已知来源添加 dns-prefetch / preconnect
│ │ ├── TCP/TLS 耗时长? --> 启用 HTTP/2,检查边缘节点部署,保持长连接
│ │ └── 等待(服务器)耗时长? --> 对后端做性能分析,检查查询和缓存
│ └── 渲染阻塞资源? --> 检查网络瀑布图中 CSS/JS 的阻塞情况
├── 交互感觉卡顿
│ ├── 点击时界面冻结? --> 分析主线程,查找长任务(>50ms)
│ ├── 表单输入延迟? --> 检查重复渲染、受控组件的开销
│ └── 动画卡顿? --> 检查布局抖动、强制回流
├── 页面导航后
│ ├── 数据加载? --> 测量 API 响应时间,检查瀑布请求链
│ └── 客户端渲染? --> 分析组件渲染时间,检查是否存在 N+1 请求
└── 后端 / API
├── 单个端点慢? --> 分析数据库查询,检查索引
├── 所有端点都慢? --> 检查连接池、内存、CPU
└── 间歇性慢? --> 检查锁竞争、GC 暂停、外部依赖
```
### 步骤 2:识别瓶颈
按类别划分的常见瓶颈:
**前端:**
| 症状 | 可能原因 | 调查方法 |
|---------|-------------|---------------|
| LCP 慢 | 大图、渲染阻塞资源、服务器慢 | 检查网络瀑布图、图片尺寸 |
| CLS 高 | 未指定尺寸的图片、延迟加载的内容、字体偏移 | 检查布局偏移归因 |
| INP 差 | 主线程上繁重的 JavaScript、大型 DOM 更新 | 检查 Performance 追踪中的长任务 |
| 初始加载慢 | 打包文件过大、网络请求过多 | 检查打包体积、代码分割 |
**后端:**
| 症状 | 可能原因 | 调查方法 |
|---------|-------------|---------------|
| API 响应慢 | N+1 查询、缺少索引、查询未优化 | 检查数据库查询日志 |
| 内存增长 | 引用泄漏、无界缓存、大数据包 | 堆快照分析 |
| CPU 飙升 | 同步重型计算、正则回溯 | CPU 性能分析 |
| 高延迟 | 缺少缓存、重复计算、网络跳数 | 在整个堆栈中追踪请求 |
### 步骤 3:修复常见反模式
#### N+1 查询(后端)
```typescript
// 错误:N+1 —— 每个任务都做一次查询来获取所属用户
const tasks = await db.tasks.findMany();
for (const task of tasks) {
task.owner = await db.users.findUnique({ where: { id: task.ownerId } });
}
// 正确:单次查询包含关联
const tasks = await db.tasks.findMany({
include: { owner: true },
});
```
#### 无限制的数据获取
```typescript
// 错误:获取所有记录
const allTasks = await db.tasks.findMany();
// 正确:分页并限制条数
const tasks = await db.tasks.findMany({
take: 20,
skip: (page - 1) * 20,
orderBy: { createdAt: 'desc' },
});
```
#### 缺少图片优化(前端)
```html
```
#### 不必要的重复渲染(React)
```tsx
// 错误:每次渲染都创建新对象,导致子组件重复渲染
function TaskList() {
return