Loading...
Loading...
Compare original and translation side by side
| Metric | Good | Needs Improvement | 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 |
| 指标 | 优秀 | 需要优化 | 较差 |
|---|---|---|---|
| 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 |
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 regression1. MEASURE → 用真实数据建立基准线
2. IDENTIFY → 找到实际瓶颈(而非假设的)
3. FIX → 解决特定瓶颈
4. VERIFY → 再次测量,确认优化效果
5. GUARD → 增加监控或测试防止性能回退undefinedundefined
**Backend:**
```bash
**后端:**
```bashundefinedundefinedWhat is slow?
├── First page load
│ ├── Large bundle? --> Measure bundle size, check code splitting
│ ├── Slow server response? --> Measure TTFB, check API/database
│ └── Render-blocking resources? --> Check network waterfall for CSS/JS blocking
├── Interaction feels sluggish
│ ├── UI freezes on click? --> Profile main thread, look for long tasks (>50ms)
│ ├── Form input lag? --> Check re-renders, controlled component overhead
│ └── Animation jank? --> Check layout thrashing, forced reflows
├── Page after navigation
│ ├── Data loading? --> Measure API response times, check for waterfalls
│ └── Client rendering? --> Profile component render time, check for N+1 fetches
└── Backend / API
├── Single endpoint slow? --> Profile database queries, check indexes
├── All endpoints slow? --> Check connection pool, memory, CPU
└── Intermittent slowness? --> Check for lock contention, GC pauses, external deps什么环节慢?
├── 首次页面加载
│ ├── 包体积过大? --> 测量包体积,检查代码拆分
│ ├── 服务端响应慢? --> 测量TTFB,检查API/数据库
│ └── 渲染阻塞资源? --> 检查网络瀑布流中的CSS/JS阻塞情况
├── 交互卡顿
│ ├── 点击后UI冻结? --> 分析主线程,查找长任务(>50ms)
│ ├── 表单输入延迟? --> 检查重渲染、受控组件开销
│ └── 动画掉帧? --> 检查布局抖动、强制回流
├── 导航后页面加载慢
│ ├── 数据加载问题? --> 测量API响应时间,检查瀑布流请求
│ └── 客户端渲染问题? --> 分析组件渲染时间,检查N+1请求
└── 后端 / API
├── 单个接口慢? --> 分析数据库查询,检查索引
├── 所有接口都慢? --> 检查连接池、内存、CPU
└── 偶发缓慢? --> 检查锁竞争、GC停顿、外部依赖| Symptom | Likely Cause | Investigation |
|---|---|---|
| Slow LCP | Large images, render-blocking resources, slow server | Check network waterfall, image sizes |
| High CLS | Images without dimensions, late-loading content, font shifts | Check layout shift attribution |
| Poor INP | Heavy JavaScript on main thread, large DOM updates | Check long tasks in Performance trace |
| Slow initial load | Large bundle, many network requests | Check bundle size, code splitting |
| Symptom | Likely Cause | Investigation |
|---|---|---|
| Slow API responses | N+1 queries, missing indexes, unoptimized queries | Check database query log |
| Memory growth | Leaked references, unbounded caches, large payloads | Heap snapshot analysis |
| CPU spikes | Synchronous heavy computation, regex backtracking | CPU profiling |
| High latency | Missing caching, redundant computation, network hops | Trace requests through the stack |
| 症状 | 可能原因 | 排查方向 |
|---|---|---|
| LCP慢 | 图片过大、渲染阻塞资源、服务端慢 | 检查网络瀑布流、图片体积 |
| CLS高 | 图片无尺寸、内容延迟加载、字体偏移 | 检查布局偏移归因 |
| INP差 | 主线程JavaScript执行压力大、DOM更新量过大 | 检查性能追踪中的长任务 |
| 首次加载慢 | 包体积大、网络请求过多 | 检查包体积、代码拆分 |
| 症状 | 可能原因 | 排查方向 |
|---|---|---|
| API响应慢 | N+1查询、缺失索引、查询未优化 | 检查数据库查询日志 |
| 内存持续增长 | 引用泄露、无边界缓存、载荷过大 | 堆快照分析 |
| CPU突增 | 同步 heavy 计算、正则回溯 | CPU性能分析 |
| 高延迟 | 缺失缓存、冗余计算、网络跳数过多 | 全链路请求追踪 |
// 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 },
});// 反面案例: 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 },
});// 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' },
});// 反面案例: 拉取所有记录
const allTasks = await db.tasks.findMany();
// 正面案例: 带限制的分页查询
const tasks = await db.tasks.findMany({
take: 20,
skip: (page - 1) * 20,
orderBy: { createdAt: 'desc' },
});<!-- BAD: No dimensions, no lazy loading, no responsive sizes -->
<img src="/hero.jpg" />
<!-- GOOD: Responsive, lazy-loaded, properly sized -->
<img
src="/hero.jpg"
srcset="/hero-400.webp 400w, /hero-800.webp 800w, /hero-1200.webp 1200w"
sizes="(max-width: 768px) 100vw, 50vw"
width="1200"
height="600"
loading="lazy"
alt="Hero image description"
/><!-- 反面案例: 无尺寸、无懒加载、无响应式尺寸 -->
<img src="/hero.jpg" />
<!-- 正面案例: 响应式、懒加载、尺寸正确 -->
<img
src="/hero.jpg"
srcset="/hero-400.webp 400w, /hero-800.webp 800w, /hero-1200.webp 1200w"
sizes="(max-width: 768px) 100vw, 50vw"
width="1200"
height="600"
loading="lazy"
alt="Hero图片描述"
/>// BAD: Creates new object on every render, causing children to re-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>;
}// 反面案例: 每次渲染都创建新对象,导致子组件重渲染
function TaskList() {
return <TaskFilters options={{ sortBy: 'date', order: 'desc' }} />;
}
// 正面案例: 稳定的引用
const DEFAULT_OPTIONS = { sortBy: 'date', order: 'desc' } as const;
function TaskList() {
return <TaskFilters options={DEFAULT_OPTIONS} />;
}
// 给开销大的组件使用React.memo
const TaskItem = React.memo(function TaskItem({ task }: Props) {
return <div>{/* 开销大的渲染逻辑 */}</div>;
});
// 给开销大的计算使用useMemo
function TaskStats({ tasks }: Props) {
const stats = useMemo(() => calculateStats(tasks), [tasks]);
return <div>{stats.completed} / {stats.total}</div>;
}// BAD: Importing entire library
import { format } from 'date-fns';
// GOOD: Tree-shakable import (if the library supports it)
import { format } from 'date-fns/format';
// GOOD: Dynamic import for heavy, rarely-used features
const ChartLibrary = lazy(() => import('./ChartLibrary'));// 反面案例: 导入整个库
import { format } from 'date-fns';
// 正面案例: 支持tree-shaking的导入(如果库支持)
import { format } from 'date-fns/format';
// 正面案例: 对不常用的重型功能做动态导入
const ChartLibrary = lazy(() => import('./ChartLibrary'));// 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;
}
// HTTP caching headers for static assets
app.use('/static', express.static('public', {
maxAge: '1y', // Cache for 1 year
immutable: true, // Never revalidate (use content hashing in filenames)
}));
// Cache-Control for API responses
res.set('Cache-Control', 'public, max-age=300'); // 5 minutes// 缓存读多改少的数据
const CACHE_TTL = 5 * 60 * 1000; // 5分钟
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;
}
// 静态资源的HTTP缓存头
app.use('/static', express.static('public', {
maxAge: '1y', // 缓存1年
immutable: true, // 无需重新验证(文件名使用内容哈希)
}));
// API响应的Cache-Control
res.set('Cache-Control', 'public, max-age=300'); // 5分钟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: ≥ 90undefinedJavaScript包: gzip后 < 200KB(首次加载)
CSS: gzip后 < 50KB
图片: 首屏单张图片 < 200KB
字体: 总计 < 100KB
API响应时间: p95 < 200ms
可交互时间: 4G网络下 < 3.5s
Lighthouse性能得分: ≥ 90undefinedundefinedundefined| Rationalization | Reality |
|---|---|
| "We'll optimize later" | Performance debt compounds. Fix obvious anti-patterns now, defer micro-optimizations. |
| "It's fast on my machine" | Your machine isn't the user's. Profile on representative hardware and networks. |
| "This optimization is obvious" | If you didn't measure, you don't know. Profile first. |
| "Users won't notice 100ms" | Research shows 100ms delays impact conversion rates. Users notice more than you think. |
| "The framework handles performance" | Frameworks prevent some issues but can't fix N+1 queries or oversized bundles. |
| 误区 | 实际情况 |
|---|---|
| "我们之后再优化" | 性能债务会越积越多。现在就修复明显的反模式,微优化可以延后。 |
| "我机器上跑得很快" | 你的机器不等于用户的设备。请在代表性的硬件和网络环境下分析。 |
| "这个优化效果是明显的" | 如果你没有测量,你就无法确认效果。先做性能分析。 |
| "用户不会注意到100ms的延迟" | 研究表明100ms的延迟就会影响转化率。用户的感知比你想象的更敏锐。 |
| "框架会处理性能问题" | 框架可以避免部分问题,但无法修复N+1查询或者过大的包体积。 |
React.memouseMemoReact.memouseMemo