关于 performance-optimization
这是一项用于诊断和改善应用性能的开发技能,要求遵循“测量—定位—优化—验证”流程。它指导开发者分析实际瓶颈,并仅保留有可测量效果的改动。
主要功能
- 01改动前记录性能基线
- 02按系统层定位瓶颈
- 03每次只做一项针对性改动
- 04通过重复测量验证效果
- 05缓存与构建包优化指南
- 06数据库、Web Vitals 与负载测试
适用场景
- 01排查前端渲染缓慢
- 02依据查询计划优化数据库查询
- 03评估 API 在负载下的表现
- 04检查优化是否改善性能指标
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ pixel-process-ug/ superkit-agents --skill performance-optimization安装后在 Claude Code 中输入/performance-optimization确认已加载。
附带文件performance-budgets.md
详细介绍
性能优化
概述
使用以测量为依据的方法,系统地识别并解决性能瓶颈。本技能严格遵循 MEASURE-IDENTIFY-OPTIMIZE-VERIFY 循环,避免过早优化和臆测。每项优化都必须带来可测量的改进,否则就应回滚。
开始时声明:“我正在使用性能优化技能来诊断并解决瓶颈。”
阶段 1:MEASURE(建立基线)
目标: 在进行任何更改之前,捕获真实指标。
操作
# Web: Lighthouse CI
npx lighthouse https://your-app.com --output=json --output-path=baseline.json
# API: load test with k6
k6 run --out json=baseline.json loadtest.js
# Database: slow query log
# PostgreSQL: SET log_min_duration_statement = 100; -- log queries > 100ms
记录这些数值。它们是衡量改进情况的基线。
停止 — 在继续阶段 2 之前,必须:
- 捕获并保存基线指标
- 定义具体的指标目标(例如,LCP < 2.5s)
- 记录测量方法(以便重复测量)
阶段 2:IDENTIFY(找到实际瓶颈)
目标: 使用性能分析工具找出时间花在了哪里。不要猜测。
性能分析工具选择表
| 层级 | 工具 | 显示内容 |
|---|---|---|
| 前端渲染 | React DevTools Profiler、Chrome Performance 选项卡 | 组件渲染耗时 |
| 网络 | Chrome Network 选项卡、WebPageTest | 请求瀑布图、TTFB |
| JavaScript | Chrome Performance 选项卡、console.time() | 函数执行时间 |
| Node.js 服务 | --prof 标志、clinic.js、0x | CPU 火焰图 |
| 数据库 | EXPLAIN ANALYZE、pg_stat_statements | 查询计划、慢查询 |
| 内存 | Chrome Memory 选项卡、heapdump | 分配模式、泄漏 |
| 构建包大小 | webpack-bundle-analyzer、vite-bundle-visualizer | 模块大小 |
瓶颈几乎从来都不在你以为的地方。先测量。
停止 — 在继续阶段 3 之前,必须:
- 使用适用于该层级的性能分析工具
- 根据数据识别出具体瓶颈
- 确认该瓶颈占问题的显著部分
阶段 3:OPTIMIZE(修复已识别的瓶颈)
目标: 应用有针对性的修复。每次只更改一项。
优化决策表
| 瓶颈类型 | 优化方法 | 示例 |
|---|---|---|
| 构建包过大 | 代码拆分、摇树优化、动态导入 | React.lazy(() => import('./HeavyComponent')) |
| API 响应缓慢 | 缓存、查询优化、分页 | 添加 TTL 为 5min 的 Redis 缓存 |
| 数据库查询缓慢 | 添加索引、优化查询计划、物化视图 | CREATE INDEX idx_user_email ON users(email) |
| 过度重新渲染 | 记忆化、虚拟化、状态重构 | React.memo、useMemo |
| 图片过大 | 压缩、延迟加载、自适应图片 | <img loading="lazy" srcset="..."> |
| TTFB 缓慢 | 服务端缓存、CDN、边缘渲染 | stale-while-revalidate 模式 |
| 内存泄漏 | 修复事件监听器清理、使用弱引用 | 正确清理 useEffect |
停止 — 在继续阶段 4 之前,必须:
- 只进行了一项更改
- 更改直接针对已识别的瓶颈
- 优化过程中没有同时进行无关更改
阶段 4:VERIFY(再次测量)
目标: 使用与阶段 1 完全相同的测量方式重新测量。
操作
- 执行与阶段 1 相同的性能分析或测量
- 比较结果:
- 指标是否有所改善?
- 改善了多少?
- 其他指标是否出现退化?
- 如果无法测量到改进,就回滚更改。
无法测量的优化不算优化。
停止 — 满足以下条件时,验证完成:
- 使用与阶段 1 相同的测量方法
- 改进已量化(例如,“LCP 从 3.2s 降至 2.1s”)
- 其他指标没有退化
- 如果没有改进:已回滚更改
缓存策略决策表
| 缓存类型 | 使用场景 | TTL 指引 | 失效方式 |
|---|---|---|---|
| 内存(LRU) | 单实例、热点数据、计算值 | 几秒到几分钟 | 淘汰策略 |
| Redis/Memcached | 多实例、共享缓存、会话 | 几分钟到几小时 | 基于事件或 TTL |
| CDN | 静态资源、公开页面、API 响应 | 几小时到几天 | 部署触发清除 |
| 浏览器 | 重复访问、静态资源 | 几天到几个月(带版本号) | 缓存破坏哈希 |
Cache-Control 响应头
# Immutable assets (hashed filenames)
Cache-Control: public, max-age=31536000, immutable
# API responses (cacheable but must revalidate)
Cache-Control: public, max-age=0, must-revalidate
ETag: "abc123"
# Private user data
Cache-Control: private, no-store
# Stale-while-revalidate (fast response + background refresh)
Cache-Control: public, max-age=60, stale-while-revalidate=300
包优化技术
| 技术 | 影响 | 实现方式 |
|---|---|---|
| 路由级代码拆分 | 高 | 每个路由使用 React.lazy() + Suspense |
| Tree shaking | 高 | 仅使用 ES 模块,设置 sideEffects: false |
| 动态导入 | 中 | 在用户操作时执行 await import('heavy-lib') |
| 图片优化 | 高 | next/image、WebP/AVIF、响应式 srcset |
| 字体优化 | 中 | next/font、font-display: swap、子集 |
| 替换依赖项 | 中 | 用 day.js 替代 moment.js,用 lodash-es 替代 lodash |
包分析命令
# Webpack
npx webpack-bundle-analyzer stats.json
# Vite
npx vite-bundle-visualizer
# Next.js
ANALYZE=true next build
数据库查询调优
索引优化
-- Find missing indexes (PostgreSQL)
SELECT schemaname, tablename, seq_scan, idx_scan
FROM pg_stat_user_tables
WHERE seq_scan > idx_scan
ORDER BY seq_scan DESC;
索引规则
| 规则 | 说明 |
|---|---|
| 为 WHERE、JOIN、ORDER BY 列创建索引 | 这些是数据库搜索的列 |
| 复合索引中将等值列放在前面 | 优先按筛选效果最显著的列过滤 |
| 复合索引中将范围列放在最后 | 筛选效果较弱,在等值过滤后应用 |
| 删除未使用的索引 | 它们会降低写入速度 |
| 为筛选查询使用部分索引 | 索引更小,查找更快 |
查询计划警示信号
| EXPLAIN ANALYZE 中的警示信号 | 含义 | 修复方法 |
|---|---|---|
| 大表上的 Seq Scan | 全表扫描 | 添加索引 |
| 涉及大量行的 Nested Loop | O(n*m) 连接 | 添加索引或重构查询 |
| 内存占用高的 Sort | 在内存中排序 | 添加与 ORDER BY 匹配的索引 |
| 实际行数 >> 估算行数 | 统计信息过时 | 运行 ANALYZE |
| 构建规模很大的 Hash Join | 内存密集型操作 | 确保连接列已建立索引 |
Web Vitals 目标
| 指标 | 良好 | 需要改进 | 较差 |
|---|---|---|---|
| LCP(最大内容绘制) | < 2.5s | 2.5-4s | > 4s |
| INP(下一次绘制交互) | < 200ms | 200-500ms | > 500ms |
| CLS(累积布局偏移) | < 0.1 | 0.1-0.25 | > 0.25 |
Web Vitals 优化表
| 指标 | 优化措施 | 实现方式 |
|---|---|---|
| LCP | 预加载 LCP 资源 | 使用 <link rel="preload"> 或 fetchpriority="high" |
| LCP | 内联关键 CSS | 提取首屏 CSS 并内联 |
| LCP | 优化 TTFB | CDN、边缘渲染、服务器缓存 |
| INP | 拆分长任务 | requestIdleCallback、scheduler.yield() |
| INP | 对输入处理器进行防抖 | 对耗时的处理器使用 100-300ms 防抖 |
| INP | Web Workers | 将计算移出主线程 |
| CLS | 明确设置尺寸 | 为图片和视频设置 width/height |
| CLS | 为动态内容预留空间 | 为广告、嵌入内容设置占位尺寸 |
| CLS | 使用 transform 动画 | 避免触发布局的属性 |
负载测试
测试类型
| 类型 | 用户数 | 持续时间 | 目的 |
|---|---|---|---|
| 冒烟测试 | 1-2 | 1 分钟 | 验证测试正常运行 |
| 负载测试 | 预期流量 | 10-30 分钟 | 检查正常性能 |
| 压力测试 | 预期流量的 2-3 倍 | 10-30 分钟 | 找出崩溃点 |
| 持续测试 | 正常负载 | 2-8 小时 | 查找内存泄漏 |
关键指标
- 响应时间百分位数(p50、p95、p99),而非平均值
- 负载下的错误率
- 吞吐量(每秒请求数)
- 资源利用率(CPU、内存、连接数)
反模式 / 常见错误
| 反模式 | 错误原因 | 正确做法 |
|---|---|---|
| 未测量就进行优化 | 不知道该修复什么 | 始终先进行测量 |
| 过早优化 | 在非瓶颈处浪费时间 | 通过性能分析找出真正的瓶颈 |
| 对所有内容都进行记忆化 | 没有经过验证的收益,却增加了复杂性 | 先进行性能分析,再进行记忆化 |
| 缓存时未制定失效策略 | 过期数据会导致错误 | 添加缓存前先定义失效策略 |
| 优化平均值而非百分位数 | 平均值会掩盖长尾延迟 | 跟踪 p95 和 p99 |
| 同时进行多项优化 | 无法确定改进来自哪一项 | 一次只做一项更改 |
| 保留没有带来可测量收益的优化 | 会产生无用代码并增加复杂性 | 如果没有可测量的改进,就回退更改 |
| 未检查查询模式就添加索引 | 未使用的索引会拖慢写入 | 先检查慢查询日志 |
子 Agent 调度机会
| 任务模式 | 调度对象 | 时机 |
|---|---|---|
| 并行分析不同系统层 | Agent 工具,设置 subagent_type="Explore"(每层一个) | 独立分析前端、后端和数据库时 |
| 包分析和 tree-shaking 审查 | Agent 工具,设置 subagent_type="general-purpose" | 关注前端包体积时 |
| 数据库查询优化分析 | 使用 Agent 工具调度 database-architect Agent | 在多个表中发现慢查询时 |
调度时请遵循 dispatching-parallel-agents 技能协议。
集成点
| 技能 | 关联 |
|---|---|
senior-frontend | 前端性能优化涉及包体积和 Web Vitals |
senior-backend | 后端性能优化涉及缓存和查询调优 |
testing-strategy | 负载测试属于测试金字塔的一部分 |
code-review | 审查时检查性能回归 |
systematic-debugging | 性能问题遵循相同的调查方法 |
acceptance-testing | 性能目标会成为验收标准 |
技能类型
灵活 — 根据项目背景调整优化深度。每次优化都必须遵循 MEASURE-IDENTIFY-OPTIMIZE-VERIFY 循环。回退任何未带来可测量改进的更改。
评论
支持 Markdown