name: performance-optimization description: 通过代码分割、懒加载、缓存策略、包大小减少、渲染优化和性能分析来优化应用性能。用于改善页面加载时间、减少包大小、优化React渲染、实现代码分割、配置缓存策略、懒加载组件和路由、优化图像和资源、分析性能瓶颈、实现大型列表的虚拟滚动,或提升核心Web指标和Lighthouse分数。
性能优化 - 让软件更快
何时使用此技能
- 改善缓慢的页面加载时间和性能
- 减少JavaScript包大小
- 使用记忆化优化React组件渲染
- 实现代码分割和懒加载
- 配置浏览器和服务器端缓存
- 使用next/image或类似工具优化图像
- 使用DevTools分析性能瓶颈
- 为大型数据集实现虚拟滚动
- 优化数据库查询和N+1问题
- 提升核心Web指标(LCP、FID、CLS)
- 实现渐进式图像加载
- 减少交互时间(TTI)
何时使用此技能
- 应用程序慢,用户抱怨延迟,或需要改善响应时间、吞吐量或资源使用。
- 当处理相关任务或功能时
- 在需要此专业知识的开发过程中
使用时机:应用程序慢,用户抱怨延迟,或需要改善响应时间、吞吐量或资源使用。
核心原则
- 先测量,后优化 - 永远不要猜测瓶颈
- 80/20规则 - 20%的代码导致80%的性能问题
- 过早优化是邪恶的 - 先让它工作,再让它正确,最后让它快
- 分析,不要假设 - 惊喜常在;你的直觉往往是错的
- 设定性能预算 - 在优化前定义可接受的限制
性能测量
建立基线
# Web指标(前端)
- FCP(首次内容绘制):< 1.8秒
- LCP(最大内容绘制):< 2.5秒
- FID(首次输入延迟):< 100毫秒
- CLS(累计布局偏移):< 0.1
- TTFB(首字节时间):< 600毫秒
# 后端
- API响应时间:< 200毫秒(p95)
- 数据库查询时间:< 50毫秒(p95)
- 吞吐量:每秒请求数
- 错误率:< 0.1%
分析工具
# 前端
- Chrome DevTools性能标签
- Lighthouse CI
- WebPageTest
- webpack-bundle-analyzer
# 后端
- Node.js:node --prof, clinic.js
- Python:cProfile, py-spy
- 数据库:EXPLAIN ANALYZE, 慢查询日志
- APM:New Relic, Datadog, Sentry Performance
# 系统
- top, htop(CPU/内存)
- iostat(磁盘I/O)
- netstat, iftop(网络)
前端性能
1. 减少JavaScript包大小
// 之前 - 导入整个库
import _ from 'lodash'; // 70KB
import moment from 'moment'; // 230KB
// 之后 - 支持树摇动的导入
import debounce from 'lodash/debounce'; // 2KB
import { format } from 'date-fns'; // 13KB
// 代码分割 - 按需加载
const HeavyComponent = lazy(() => import('./HeavyComponent'));
// 动态导入
button.onclick = async () => {
const module = await import('./analytics');
module.trackEvent('button_click');
};
2. 优化图像
<!-- 之前 - 未优化 -->
<img src="photo.jpg" alt="产品" />
<!-- 之后 - 响应式及现代格式 -->
<picture>
<source srcset="photo.avif" type="image/avif">
<source srcset="photo.webp" type="image/webp">
<img
src="photo.jpg"
alt="产品"
loading="lazy"
width="800"
height="600"
srcset="photo-400.jpg 400w, photo-800.jpg 800w, photo-1200.jpg 1200w"
sizes="(max-width: 600px) 400px, (max-width: 1200px) 800px, 1200px"
/>
</picture>
<!-- 或使用Next.js Image组件 -->
<Image
src="/photo.jpg"
alt="产品"
width={800}
height={600}
placeholder="blur"
quality={85}
/>
3. 懒加载和代码分割
// React - 懒加载路由
const Dashboard = lazy(() => import('./Dashboard'));
const Settings = lazy(() => import('./Settings'));
function App() {
return (
<Suspense fallback={<Loading />}>
<Routes>
<Route path="/dashboard" element={<Dashboard />} />
<Route path="/settings" element={<Settings />} />
</Routes>
</Suspense>
);
}
// Next.js - 自动代码分割
// 只需使用动态导入
import dynamic from 'next/dynamic';
const DynamicChart = dynamic(() => import('./Chart'), {
loading: () => <Spinner />,
ssr: false // 不在服务器上渲染
});
4. 记忆化和缓存
// React - 防止不必要的重新渲染
const ExpensiveComponent = memo(({ data }) => {
return <div>{/* 复杂渲染 */}</div>;
});
// 记忆化昂贵的计算
function ProductList({ products, filters }) {
const filteredProducts = useMemo(() => {
return products.filter(p => matchesFilters(p, filters));
}, [products, filters]); // 仅在依赖项变化时重新计算
return <div>{filteredProducts.map(renderProduct)}</div>;
}
// 记忆化回调以防止子组件重新渲染
function Parent() {
const handleClick = useCallback(() => {
console.log('点击了');
}, []); // 稳定的函数引用
return <Child onClick={handleClick} />;
}
5. 长列表的虚拟化
// 之前 - 渲染10,000项(慢!)
function ProductList({ products }) {
return (
<div>
{products.map(product => (
<ProductCard key={product.id} product={product} />
))}
</div>
);
}
// 之后 - 只渲染可见项
import { FixedSizeList } from 'react-window';
function ProductList({ products }) {
return (
<FixedSizeList
height={600}
itemCount={products.length}
itemSize={100}
width="100%"
>
{({ index, style }) => (
<div style={style}>
<ProductCard product={products[index]} />
</div>
)}
</FixedSizeList>
);
}
后端性能
1. 数据库查询优化
-- 之前 - N+1查询问题
-- 先获取用户,然后为每个用户单独查询帖子
SELECT * FROM users;
-- 然后对每个用户:
SELECT * FROM posts WHERE user_id = ?;
-- 之后 - 连接或贪婪加载
SELECT
users.*,
posts.id as post_id,
posts.title as post_title
FROM users
LEFT JOIN posts ON posts.user_id = users.id;
-- 为频繁查询的列添加索引
CREATE INDEX idx_posts_user_id ON posts(user_id);
CREATE INDEX idx_posts_created_at ON posts(created_at);
-- 常见查询模式的复合索引
CREATE INDEX idx_posts_user_created
ON posts(user_id, created_at DESC);
2. 缓存策略
// 内存缓存用于昂贵计算
const cache = new Map();
async function getExpensiveData(key) {
if (cache.has(key)) {
return cache.get(key);
}
const data = await expensiveComputation(key);
cache.set(key, data);
// 5分钟后过期
setTimeout(() => cache.delete(key), 5 * 60 * 1000);
return data;
}
// Redis缓存用于分布式系统
import Redis from 'ioredis';
const redis = new Redis();
async function getCachedUserProfile(userId) {
const cacheKey = `user:${userId}:profile`;
// 先尝试缓存
const cached = await redis.get(cacheKey);
if (cached) {
return JSON.parse(cached);
}
// 缓存未命中 - 从数据库获取
const profile = await db.users.findById(userId);
// 存储到缓存(1小时后过期)
await redis.setex(cacheKey, 3600, JSON.stringify(profile));
return profile;
}
// HTTP缓存头
app.get('/api/products', (req, res) => {
res.set({
'Cache-Control': 'public, max-age=300', // 5分钟
'ETag': generateETag(data)
});
res.json(products);
});
3. 数据库连接池
// 之前 - 每个查询新连接(慢!)
async function getUser(id) {
const connection = await mysql.createConnection(config);
const [rows] = await connection.execute('SELECT * FROM users WHERE id = ?', [id]);
await connection.end();
return rows[0];
}
// 之后 - 连接池
import mysql from 'mysql2/promise';
const pool = mysql.createPool({
host: 'localhost',
user: 'root',
database: 'mydb',
waitForConnections: true,
connectionLimit: 10,
queueLimit: 0
});
async function getUser(id) {
const [rows] = await pool.execute('SELECT * FROM users WHERE id = ?', [id]);
return rows[0];
}
// NeonDB无服务器 - 使用@neondatabase/serverless
import { Pool } from '@neondatabase/serverless';
const pool = new Pool({ connectionString: process.env.DATABASE_URL });
4. 分页和限制
// 之前 - 获取所有记录(内存爆炸!)
async function getProducts() {
return await db.products.findAll(); // 可能数百万行
}
// 之后 - 基于游标的分页
async function getProducts({ cursor, limit = 20 }) {
return await db.products.findMany({
take: limit,
skip: cursor ? 1 : 0,
cursor: cursor ? { id: cursor } : undefined,
orderBy: { createdAt: 'desc' }
});
}
// 偏移分页(更简单但对深层页面慢)
async function getProducts({ page = 1, limit = 20 }) {
const offset = (page - 1) * limit;
return await db.products.findMany({
take: limit,
skip: offset,
orderBy: { createdAt: 'desc' }
});
}
5. 异步处理和作业队列
// 之前 - 阻塞请求直到邮件发送
app.post('/signup', async (req, res) => {
const user = await createUser(req.body);
await sendWelcomeEmail(user.email); // 阻塞2-3秒!
res.json({ success: true });
});
// 之后 - 队列作业,立即响应
import { Queue } from 'bullmq';
const emailQueue = new Queue('emails', {
connection: { host: 'localhost', port: 6379 }
});
app.post('/signup', async (req, res) => {
const user = await createUser(req.body);
// 将邮件加入队列异步发送
await emailQueue.add('welcome', {
to: user.email,
userId: user.id
});
res.json({ success: true }); // 快速响应!
});
// 工作进程在后台处理作业
const worker = new Worker('emails', async (job) => {
await sendEmail(job.data.to, 'welcome', { userId: job.data.userId });
});
算法优化
选择正确的数据结构
// 之前 - O(n)查找
const activeUsers = [];
function isActive(userId) {
return activeUsers.includes(userId); // 线性搜索
}
// 之后 - O(1)查找
const activeUsers = new Set();
function isActive(userId) {
return activeUsers.has(userId); // 常数时间
}
// 之前 - 频繁在开头插入/删除时为O(n)
const queue = [];
queue.unshift(item); // O(n) - 移动整个数组
// 之后 - 使用适当数据结构的O(1)
class Queue {
constructor() {
this.items = {};
this.head = 0;
this.tail = 0;
}
enqueue(item) {
this.items[this.tail] = item;
this.tail++;
}
dequeue() {
const item = this.items[this.head];
delete this.items[this.head];
this.head++;
return item;
}
}
减少计算复杂度
// 之前 - O(n²)嵌套循环
function findDuplicates(arr) {
const duplicates = [];
for (let i = 0; i < arr.length; i++) {
for (let j = i + 1; j < arr.length; j++) {
if (arr[i] === arr[j]) {
duplicates.push(arr[i]);
}
}
}
return duplicates;
}
// 之后 - 使用Set的O(n)
function findDuplicates(arr) {
const seen = new Set();
const duplicates = new Set();
for (const item of arr) {
if (seen.has(item)) {
duplicates.add(item);
}
seen.add(item);
}
return Array.from(duplicates);
}
监控和告警
添加性能指标
import { performance } from 'perf_hooks';
async function processOrder(order) {
const startTime = performance.now();
try {
const result = await expensiveProcessing(order);
const duration = performance.now() - startTime;
// 记录慢操作
if (duration > 1000) {
logger.warn('订单处理慢', {
orderId: order.id,
duration
});
}
// 发送指标到监控服务
metrics.histogram('order_processing_time', duration, {
status: 'success'
});
return result;
} catch (error) {
const duration = performance.now() - startTime;
metrics.histogram('order_processing_time', duration, {
status: 'error'
});
throw error;
}
}
性能检查清单
前端:
□ 包大小 < 200KB(gzip压缩后)
□ 图像优化(WebP/AVIF)
□ 对折叠以下内容懒加载
□ 路由代码分割
□ 长列表虚拟化
□ 昂贵计算记忆化
□ 设置HTTP缓存头
□ 关键CSS内联
后端:
□ 数据库查询索引化
□ 消除N+1查询
□ 配置连接池
□ 响应分页
□ 繁重操作队列化
□ 实现响应缓存
□ 启用Gzip压缩
□ 静态资源使用CDN
一般:
□ 定义性能预算
□ 配置监控和告警
□ CI中定期性能测试
□ 在真实数据上进行分析
资源
记住:快速的软件让用户满意。测量、优化瓶颈,并持续监控。