# 性能检查清单

Web 应用性能的快速参考清单。可与 `performance-optimization` skill 配合使用。

## 目录

- [Core Web Vitals 目标](#core-web-vitals-目标)
- [前端检查清单](#前端检查清单)
- [后端检查清单](#后端检查清单)
- [测量命令](#测量命令)
- [常见反模式](#常见反模式)

## Core Web Vitals 目标

| Metric | Good | Needs Work | Poor |
|--------|------|------------|------|
| LCP (Largest Contentful Paint) | ≤ 2.5s | ≤ 4.0s | > 4.0s |
| INP (Interaction to Next Paint) | ≤ 200ms | ≤ 500ms | > 500ms |
| CLS (Cumulative Layout Shift) | ≤ 0.1 | ≤ 0.25 | > 0.25 |

## 前端检查清单

### 图片
- [ ] 图片使用现代格式（WebP、AVIF）
- [ ] 图片尺寸是响应式的（`srcset` 和 `sizes`）
- [ ] 图片有明确的 `width` 和 `height` 属性（避免 CLS）
- [ ] 首屏以下图片使用 `loading="lazy"`
- [ ] Hero / LCP 图片使用 `fetchpriority="high"`，且不懒加载

### JavaScript
- [ ] 初始加载的压缩 bundle 小于 200KB
- [ ] 对路由和重功能使用动态 `import()` 做 code splitting
- [ ] 已启用 tree shaking（生产包里没有死代码）
- [ ] `<head>` 里没有阻塞性的 JavaScript（用 `defer` 或 `async`）
- [ ] 重计算下放到 Web Workers（如果适用）
- [ ] 对会用同样 props 反复重渲染的昂贵组件使用 `React.memo()`
- [ ] 只有在 profiling 显示有收益时才用 `useMemo()` / `useCallback()`

### CSS
- [ ] 关键 CSS 已内联或预加载
- [ ] 非关键样式没有阻塞渲染
- [ ] 生产环境没有 CSS-in-JS 的 runtime 开销（使用提取）
- [ ] 已设置字体显示策略（`font-display: swap` 或 `optional`）
- [ ] 先考虑系统字体栈，再上自定义字体

### 网络
- [ ] 静态资源已用长 `max-age` + 内容哈希缓存
- [ ] API response 在合适时配置缓存（`Cache-Control`）
- [ ] 已启用 HTTP/2 或 HTTP/3
- [ ] 对已知 origin 做了预连接（`<link rel="preconnect">`）
- [ ] 没有不必要的重定向

### 渲染
- [ ] 没有 layout thrashing（强制同步布局）
- [ ] 动画使用 `transform` 和 `opacity`（GPU 加速）
- [ ] 长列表使用虚拟化（例如 `react-window`）
- [ ] 没有不必要的整页重渲染

## 后端检查清单

### 数据库
- [ ] 没有 N+1 查询模式（使用 eager loading / joins）
- [ ] 查询有合适的索引
- [ ] 列表 endpoint 已分页（不要 `SELECT * FROM table`）
- [ ] 已配置连接池
- [ ] 已开启慢查询日志

### API
- [ ] 响应时间 < 200ms（p95）
- [ ] request handler 里没有同步重计算
- [ ] 批量操作替代逐条循环调用
- [ ] 响应压缩（gzip / brotli）
- [ ] 配置了合适的缓存（内存、Redis、CDN）

### 基础设施
- [ ] 静态资源有 CDN
- [ ] 服务器离用户近（或使用 edge 部署）
- [ ] 已配置横向扩展（如需要）
- [ ] 为 load balancer 提供 health check endpoint

## 测量命令

```bash
# Lighthouse CLI
npx lighthouse https://localhost:3000 --output json --output-path ./report.json

# Bundle 分析
npx webpack-bundle-analyzer stats.json
# 或者对 Vite：
npx vite-bundle-visualizer

# 检查 bundle size
npx bundlesize

# 在代码里看 Web Vitals
import { onLCP, onINP, onCLS } from 'web-vitals';
onLCP(console.log);
onINP(console.log);
onCLS(console.log);
```

## 常见反模式

| 反模式 | 影响 | 修复 |
|---|---|---|
| N+1 查询 | 数据库负载线性增长 | 用 joins、includes 或 batch loading |
| 无界查询 | 内存耗尽、超时 | 始终分页，添加 LIMIT |
| 缺索引 | 数据增长后读很慢 | 给过滤 / 排序列加索引 |
| Layout thrashing | 卡顿、掉帧 | 批量读 DOM，再批量写 DOM |
| 图片没优化 | LCP 慢、浪费带宽 | 用 WebP、响应式尺寸、懒加载 |
| 大 bundle | Time to Interactive 慢 | code split、tree shake、审计依赖 |
| 阻塞主线程 | INP 差，界面不响应 | 用 Web Workers，延迟执行 |
| 内存泄漏 | 内存不断增长，最终崩溃 | 清理 listener、interval、ref |
