8.5 性能优化与最佳实践
性能优化的层次
性能金字塔
用户体验
╱ ╲
╱ ╲
╱ 业务层 ╲
╱ ╲
╱ 应用层 ╲
╱ ╲
╱ 系统层 ╲
╱____________________╲
╱ ╲
╱ 网络层 ╲
╱________________________╲
╱ ╲
╱ 硬件层 ╲
╱________________________________╲各层优化要点:
硬件层 (20%提升)
├─ CPU:多核并行处理
├─ 内存:增加RAM容量
├─ 存储:SSD替代HDD
└─ 网络:CDN加速
网络层 (50%提升)
├─ DNS解析:使用优质DNS服务商
├─ 建立连接:HTTP/2, HTTP/3
├─ 传输数据:压缩、缓存
└─ 地理分布:CDN全球节点
系统层 (30%提升)
├─ Web服务器:Nginx优化配置
├─ 应用服务器:连接池优化
├─ 数据库:索引、查询优化
└─ 缓存层:多级缓存策略
应用层 (70%提升)
├─ 代码层面:算法优化
├─ 数据库:减少查询次数
├─ 资源:图片压缩、懒加载
└─ 渲染:SSR、增量静态生成
业务层 (10%提升)
├─ 减少不必要请求
├─ 合并操作
├─ 异步处理
└─ 预加载关键数据前端性能优化
核心 Web 指标 (Core Web Vitals)
LCP (Largest Contentful Paint):
目标:< 2.5秒
优化策略:
1. 关键资源优化
- 图片压缩 (WebP/AVIF)
- 字体子集化
- CSS压缩
2. 资源加载优化
- 预加载关键资源
- 延迟加载非关键资源
- 使用CDN
3. 服务器优化
- 启用Gzip/Brotli压缩
- 设置合适的缓存头
- 优化服务器响应时间FID (First Input Delay) → INP (Interaction to Next Paint):
目标:< 100毫秒
优化策略:
1. 减少JavaScript执行时间
- 代码分割 (Code Splitting)
- 移除未使用代码
- 使用Web Workers
2. 优化主线程
- 避免长时间任务 (>50ms)
- 使用requestIdleCallback
- 分解大任务为小任务
3. 优化事件处理
- 防抖/节流
- 使用Passive事件监听器
- 避免强制同步布局CLS (Cumulative Layout Shift):
目标:< 0.1
优化策略:
1. 预留空间
- 固定图片尺寸
- 预留广告位
- 使用skeleton加载
2. 避免布局抖动
- 不动态插入内容
- 使用transform替代position
- 避免字体闪烁 (font-display)图片优化
响应式图片:
// Next.js Image优化
import Image from 'next/image'
// ✅ 正确:设置明确尺寸
<Image
src="/hero.jpg"
width={800}
height={600}
alt="Hero image"
priority={true} // 预加载
placeholder="blur"
blurDataURL="data:image/jpeg..."
/>
// ✅ 响应式图片
<Image
src="/responsive-image.jpg"
fill
sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw"
alt="Responsive image"
/>
// ✅ 现代格式
// 1. WebP (现代浏览器)
// 2. AVIF (更高压缩比)
// 3. PNG/JPG (回退)
<picture>
<source
srcSet="/image.avif"
type="image/avif"
/>
<source
srcSet="/image.webp"
type="image/webp"
/>
<img
src="/image.jpg"
alt="Image"
/>
</picture>代码分割
路由级别分割:
// ✅ Next.js动态导入
import dynamic from 'next/dynamic'
// 组件懒加载
const HeavyComponent = dynamic(
() => import('../components/HeavyComponent'),
{
loading: () => <p>Loading...</p>,
ssr: false // 客户端渲染
}
)
// 库懒加载
const Chart = dynamic(() => import('react-chartjs-2'), {
ssr: false
})模块级分割:
// 按需加载功能模块
const loadModule = async () => {
const { expensiveFunction } = await import('../utils/expensive')
return expensiveFunction()
}
// 使用Suspense
<Suspense fallback={<Spinner />}>
<HeavyContent />
</Suspense>React 性能优化
组件优化:
import { memo, useMemo, useCallback } from 'react'
// 1. memo 避免不必要重渲染
const ExpensiveComponent = memo(({ data, onUpdate }) => {
const processedData = useMemo(() => {
return data.map(item => ({
...item,
processed: true
}))
}, [data])
const handleClick = useCallback((id) => {
onUpdate(id)
}, [onUpdate])
return (
<div>
{processedData.map(item => (
<Item
key={item.id}
data={item}
onClick={handleClick}
/>
))}
</div>
)
})
// 2. 虚拟列表 (大量数据)
import { FixedSizeList as List } from 'react-window'
const VirtualizedList = ({ items }) => (
<List
height={600}
itemCount={items.length}
itemSize={35}
>
{({ index, style }) => (
<div style={style}>
{items[index]}
</div>
)}
</List>
)
// 3. useDeferredValue (React 18)
const SearchResults = ({ query }) => {
const deferredQuery = useDeferredValue(query)
// 优先渲染输入框,延迟搜索结果
return (
<div>
<input value={query} onChange={handleChange} />
<ExpensiveList query={deferredQuery} />
</div>
)
}服务端渲染优化
Next.js 优化:
// 页面级别缓存
export const revalidate = 3600 // 1小时
// 生成静态参数
export async function generateStaticParams() {
const posts = await fetchPosts()
return posts.map(post => ({
slug: post.slug
}))
}
// 组件级缓存
const data = await unstable_cache(
() => fetchExpensiveData(),
['cache-key'],
{ revalidate: 3600 }
)()后端性能优化
数据库优化
索引策略:
-- 1. 主键索引 (自动创建)
CREATE TABLE users (
id UUID PRIMARY KEY,
...
);
-- 2. 常用查询索引
CREATE INDEX idx_users_email ON users(email);
CREATE INDEX idx_users_status ON users(status);
-- 3. 复合索引 (多列查询)
CREATE INDEX idx_orders_user_status
ON orders(user_id, status);
-- 4. 部分索引 (条件筛选)
CREATE INDEX idx_active_users
ON users(id) WHERE status = 'active';
-- 5. 表达式索引
CREATE INDEX idx_users_lower_email
ON users(LOWER(email));查询优化:
-- ❌ 慢查询:N+1问题
SELECT * FROM users;
-- 为每个用户查询订单
SELECT * FROM orders WHERE user_id = 1;
SELECT * FROM orders WHERE user_id = 2;
-- ...
-- ✅ 快查询:JOIN优化
SELECT u.*, o.*
FROM users u
LEFT JOIN orders o ON u.id = o.user_id;
-- ✅ 批量查询
-- 1. 先查询ID
SELECT id FROM users WHERE status = 'active';
-- 2. 批量查询订单
SELECT * FROM orders
WHERE user_id IN (/* IDs */);
-- ✅ 分页优化
-- ❌ 偏移量大时慢
SELECT * FROM orders LIMIT 100000, 20;
-- ✅ 游标分页
SELECT * FROM orders
WHERE id > last_seen_id
ORDER BY id
LIMIT 20;API 优化
响应优化:
// 1. 字段选择
// ❌ 返回所有字段
const users = await db.users.findMany()
// ✅ 只返回需要的字段
const users = await db.users.findMany({
select: {
id: true,
name: true,
email: true
}
})
// 2. 数据压缩
import { gzipSync } from 'zlib'
export async function GET() {
const data = await getLargeDataset()
// 压缩响应
const compressed = gzipSync(JSON.stringify(data))
return new Response(compressed, {
headers: {
'Content-Type': 'application/json',
'Content-Encoding': 'gzip'
}
})
}
// 3. 分页响应
export async function GET(request: Request) {
const { searchParams } = new URL(request.url)
const page = parseInt(searchParams.get('page') || '1')
const limit = 20
const [data, total] = await Promise.all([
getPaginatedData(page, limit),
getTotalCount()
])
return Response.json({
data,
pagination: {
page,
limit,
total,
totalPages: Math.ceil(total / limit)
}
})
}缓存策略
多级缓存:
// L1: 内存缓存 (进程内)
const memoryCache = new Map()
async function getUser(id: string) {
// 检查内存缓存
if (memoryCache.has(id)) {
return memoryCache.get(id)
}
// L2: Redis缓存
const cached = await redis.get(`user:${id}`)
if (cached) {
const user = JSON.parse(cached)
memoryCache.set(id, user)
return user
}
// L3: 数据库查询
const user = await db.users.findUnique({ where: { id } })
// 写入缓存
await redis.setex(`user:${id}`, 3600, JSON.stringify(user))
memoryCache.set(id, user)
return user
}
// L4: 应用级缓存 (Prisma)
const user = await db.users.findUnique({
where: { id },
cache: {
// 缓存5分钟
ttl: 300,
tag: ['users']
}
})队列与异步
任务队列:
// 使用Bull队列
import Queue from 'bull'
const imageQueue = new Queue('image processing', {
redis: 'redis://localhost:6379'
})
// 添加任务
await imageQueue.add('resize', {
imageId: '123',
size: 'thumbnail'
}, {
attempts: 3,
backoff: {
type: 'exponential',
delay: 2000
}
})
// 处理任务
imageQueue.process('resize', async (job) => {
const { imageId, size } = job.data
// 图片处理逻辑
await processImage(imageId, size)
return { processed: true }
})
// 幂等性设计
// 确保重复执行不会产生副作用
async function processImage(imageId: string, size: string) {
// 检查是否已处理
const exists = await checkIfProcessed(imageId, size)
if (exists) {
return { skipped: true }
}
// 处理图片
await resizeImage(imageId, size)
// 标记为已处理
await markAsProcessed(imageId, size)
return { processed: true }
}监控与测量
性能监控指标
前端监控:
// Web Vitals 监控
import { getCLS, getFID, getFCP, getLCP, getTTFB } from 'web-vitals'
function sendToAnalytics(metric) {
// 发送到分析服务
console.log(metric)
}
getCLS(sendToAnalytics)
getFID(sendToAnalytics)
getFCP(sendToAnalytics)
getLCP(sendToAnalytics)
getTTFB(sendToAnalytics)
// 自定义性能标记
performance.mark('start-task')
// ... 执行任务
performance.mark('end-task')
performance.measure('task-duration', 'start-task', 'end-task')
const measure = performance.getEntriesByName('task-duration')[0]
console.log(`任务耗时: ${measure.duration}ms`)后端监控:
// API响应时间监控
import { performance } from 'perf_hooks'
export async function GET(request: Request) {
const start = performance.now()
try {
const data = await getData()
const duration = performance.now() - start
// 记录慢查询
if (duration > 1000) {
console.warn(`Slow query: ${duration}ms`)
}
return Response.json(data)
} catch (error) {
const duration = performance.now() - start
console.error(`Error after ${duration}ms`, error)
throw error
}
}性能预算
性能预算清单:
前端性能预算:
页面大小
├─ HTML: < 50KB (压缩后)
├─ CSS: < 100KB (压缩后)
├─ JS: < 200KB (压缩后,总计)
└─ Images: < 1MB
加载时间
├─ 首屏渲染 (FCP): < 1.8s
├─ 最大内容绘制 (LCP): < 2.5s
├─ 交互响应 (INP): < 200ms
└─ 布局稳定 (CLS): < 0.1
API性能预算:
├─ 简单查询: < 100ms
├─ 复杂查询: < 500ms
├─ 批处理任务: < 10s
└─ 长时间任务: 异步处理
数据库性能预算:
├─ 简单查询: < 10ms
├─ 复杂查询: < 100ms
├─ 索引扫描: < 50ms
└─ 全表扫描: 不允许性能分析工具
Lighthouse CI:
// lighthouserc.json
{
"ci": {
"collect": {
"url": ["http://localhost:3000"],
"numberOfRuns": 3
},
"assert": {
"assertions": {
"categories:performance": ["error", {"minScore": 0.9}],
"categories:accessibility": ["error", {"minScore": 0.9}],
"categories:best-practices": ["error", {"minScore": 0.9}],
"categories:seo": ["error", {"minScore": 0.9}]
}
}
}
}APM 工具对比:
性能监控工具
Sentry
✅ 优势:
- 错误追踪专业
- 性能监控集成
- 免费额度充足
- 易用性好
❌ 劣势:
- 功能相对简单
- 自定义能力有限
DataDog
✅ 优势:
- 功能全面
- 监控覆盖广
- 告警灵活
❌ 劣势:
- 价格昂贵
- 学习曲线陡峭
New Relic
✅ 优势:
- APM专业
- 数据可视化好
❌ 劣势:
- 价格高
- 配置复杂优化策略总结
优化优先级
立即优化 (P0):
- Core Web Vitals不达标
- 首屏加载>3秒
- API响应>1秒
- 错误率>1%
计划优化 (P1):
- 代码分割未做
- 缓存未使用
- 图片未优化
- 数据库无索引
长期优化 (P2):
- 微服务拆分
- 架构重构
- 技术栈升级
- 监控完善优化检查清单
前端优化:
□ 资源压缩 (Gzip/Brotli)
□ 图片优化 (WebP/压缩/懒加载)
□ 代码分割
□ 缓存设置
□ CDN配置
□ 字体优化
□ 关键资源预加载
□ 减少第三方依赖
后端优化:
□ 数据库索引
□ 查询优化
□ 缓存策略
□ 连接池配置
□ 异步处理
□ 负载均衡
□ 监控告警
□ 错误处理
基础设施:
□ CDN部署
□ 监控告警
□ 日志收集
□ 自动扩容
□ 健康检查
□ 灾备方案记住:性能优化是持续的过程,不是一次性任务。定期测量、监控、改进。
Last updated on