Development
性能优化
优化应用性能。在存在性能要求、怀疑出现性能回退,或需要改进 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
两种互补的方法 —— 两者都要用:
前端:
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');
前端:
| 症状 | 可能原因 | 排查方式 | |---------|-------------|---------------| | LCP 缓慢 | 大图片、阻塞渲染的资源 | 检查网络瀑布图、图片尺寸 | | CLS 偏高 | 未指定尺寸的图片、延迟加载的内容 | 检查布局偏移归因 | | INP 较差 | 主线程上有繁重的 JavaScript | 在性能跟踪中检查长任务 |
后端:
| 症状 | 可能原因 | 排查方式 | |---------|-------------|---------------| | API 响应缓慢 | N+1 查询、缺少索引 | 检查数据库查询日志 | | 内存增长 | 引用泄漏、无界缓存 | 堆快照分析 | | CPU 飙升 | 同步的繁重计算 | 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 },
});
// 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' },
});
// 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
在任何与性能相关的变更之后: