关于 performance-review
performance-review 是用于测量和评估 Web 应用性能的审查流程,面向发布前检查、性能回归排查和高流量准备。它通过浏览器测量 Core Web Vitals、页面加载、资源和运行时表现,并生成指标报告、修复建议及 YAML 回归测试。
主要功能
- 01Core Web Vitals 测量
- 02页面加载指标评估
- 03资源与 Bundle 检查
- 04运行时性能分析
- 05性能报告与 YAML 回归测试
- 06快速、默认和深入审查模式
适用场景
- 01发布前检查关键页面性能
- 02排查加载缓慢或交互迟滞
- 03评估新增依赖或第三方脚本影响
- 04为高流量活动准备性能基线
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ shiplightai/ agent-skills --skill performance-review安装后在 Claude Code 中输入/performance-review确认已加载。
详细介绍
性能评审
根据 Google 的核心网页指标阈值和行业基准,衡量并评估应用的性能。本次评审可发现开发期间难以察觉、但会影响真实用户的性能问题,例如包体积膨胀、布局偏移、交互缓慢、图片未优化以及阻塞渲染的资源。
适用场景
在以下情况下使用 /performance-review:
- 发布前或添加重大功能后
- 页面加载感觉缓慢,但不确定原因
- 为高流量活动做准备
- 添加新的依赖项或第三方脚本后
- SEO 排名取决于性能分数
- 用户反馈速度缓慢或放弃使用
参考标准
- Google 核心网页指标 — LCP、INP、CLS(2024 年阈值)
- Google Lighthouse — 性能评分方法
- HTTP Archive — Web 性能基准(中位数、p75、p90)
- Web.dev 性能指南 — 最佳实践
- RAIL 模型 — 响应、动画、空闲、加载预算
阶段概览
Phase 1: EDUCATE → Performance impact on business and what we measure
Phase 2: SCOPE → Identify key pages, performance budget, baseline
Phase 3: ANALYZE → Browser-based performance measurement
Phase 4: REPORT → Findings with metrics, scores, and comparisons
Phase 5: REMEDIATE → Fix guidance + YAML regression tests
阶段 1:说明
为什么重要: 页面加载延迟 1 秒会使转化率降低 7%(Akamai)。自 2021 年起,Google 将核心网页指标用作排名信号。加载时间超过 3 秒的页面会流失 53% 的移动访客(Google)。Amazon 发现,延迟每增加 100ms,销售额就会损失 1%。性能是一项功能,而性能缺失则是一个缺陷。
本次评审会在浏览器中衡量真实性能,而不只是进行静态分析。我们会捕获实际加载时间、渲染行为和交互响应速度。
阶段 2:确定范围
收集背景信息
-
从代码库自动检测:
- 构建系统(Webpack、Vite、Next.js 等)
- 包分析配置(如有)
- 图片优化流程(sharp、next/image 等)
- 字体加载策略
- 代码拆分配置
- Service Worker / 缓存策略
- CDN 配置
-
逐项询问用户:
- 目标 URL:应用在哪里运行?(为获得更贴近实际的测量结果,优先使用生产环境)
- 关键页面:哪些页面对性能最重要?(建议:落地页、主要功能页、数据密集型页面)
- 性能预算:是否已有目标?(默认:核心网页指标的“良好”阈值)
- 已知问题:是否有感觉缓慢的页面?(可选)
-
制定测量计划:
- 测试页面(3-5 个关键页面)
- 条件:模拟桌面端和移动端(Moto G4 / 慢速 4G)
- 指标:核心网页指标及补充指标
- 基线:首次运行建立基线以供对比
阶段 3:分析
使用 new_session 打开浏览器会话,并启用 record_evidence: true。针对范围内的每个页面,运行所有测量类别。
类别 A:核心网页指标(CWV)
| 检查 ID | 指标 | 良好 | 有待改进 | 较差 | 方法 |
|---|---|---|---|---|---|
| CWV-01 | LCP(最大内容绘制) | ≤2.5s | 2.5-4.0s | >4.0s | 使用 PerformanceObserver 监测 LCP 条目 |
| CWV-02 | INP(交互到下一次绘制) | ≤200ms | 200-500ms | >500ms | 点击关键交互元素,测量延迟 |
| CWV-03 | CLS(累积布局偏移) | ≤0.1 | 0.1-0.25 | >0.25 | 使用 PerformanceObserver 监测 layout-shift 条目 |
浏览器验证: 导航到每个页面,并通过 JavaScript 捕获指标:
// LCP
new PerformanceObserver((list) => {
const entries = list.getEntries();
const lcp = entries[entries.length - 1];
console.log('LCP:', lcp.startTime);
}).observe({ type: 'largest-contentful-paint', buffered: true });
// CLS
let clsValue = 0;
new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
if (!entry.hadRecentInput) clsValue += entry.value;
}
console.log('CLS:', clsValue);
}).observe({ type: 'layout-shift', buffered: true });
类别 B:页面加载性能(LOAD)
| 检查 ID | 检查项 | 阈值 | 方法 |
|---|---|---|---|
| LOAD-01 | 首字节时间(TTFB) | ≤800ms | performance.timing.responseStart - navigationStart |
| LOAD-02 | 首次内容绘制(FCP) | ≤1.8s | performance.getEntriesByName('first-contentful-paint') |
| LOAD-03 | DOM 内容加载完成 | ≤2.0s | performance.timing.domContentLoadedEventEnd |
| LOAD-04 | 页面总大小 | ≤3MB(移动端)/ ≤5MB(桌面端) | performance.getEntriesByType('resource') 求和 |
| LOAD-05 | HTTP 请求数 | ≤50 | 统计资源条目数 |
| LOAD-06 | 可交互时间(TTI) | ≤3.8s | 长任务分析 |
| LOAD-07 | 总阻塞时间(TBT) | ≤200ms | 长任务总和(超过 50ms 的部分) |
| LOAD-08 | 速度指数 | ≤3.4s | 视觉进度分析 |
浏览器验证: 使用 Performance API 和 performance.getEntries() 收集所有指标。
类别 C:资源优化(RES)
| 检查 ID | 检查项 | 标准 | 方法 |
|---|---|---|---|
| RES-01 | 图像使用现代格式(WebP/AVIF) | Web.dev | 检查图像 URL 和 Content-Type |
| RES-02 | 图像尺寸适当(未过大) | Web.dev | 比较显示尺寸与原始尺寸 |
| RES-03 | 图像启用延迟加载(首屏以下) | Web.dev | 检查首屏以下图像是否设置 loading="lazy" |
| RES-04 | 图像具有明确的尺寸(width/height) | 防止 CLS | 检查是否存在 width/height 属性 |
| RES-05 | CSS 不阻塞渲染(或以内联方式提供关键 CSS) | Web.dev | 检查 CSS 加载策略 |
| RES-06 | JavaScript 使用 defer 或 async 加载 | Web.dev | 检查脚本加载属性 |
| RES-07 | 字体使用 font-display: swap 或 optional | Web.dev | 检查 @font-face 声明 |
| RES-08 | 字体已预加载 | Web.dev | 检查是否存在 <link rel="preload" as="font"> |
| RES-09 | 启用 Gzip/Brotli 压缩 | HTTP 最佳实践 | 检查 Content-Encoding 标头 |
| RES-10 | 使用 HTTP/2 或 HTTP/3 | HTTP 最佳实践 | 通过 Performance API 检查协议 |
| RES-11 | 缓存标头有效 | HTTP 最佳实践 | 检查静态资源的 Cache-Control |
| RES-12 | 未加载未使用的 CSS/JS | 包效率 | 通过 Page.startJSCoverage/startCSSCoverage 检查覆盖率 |
浏览器验证: 使用 JavaScript 检查所有已加载资源的类型、大小和加载属性。使用 performance.getEntriesByType('resource') 获取详细的资源指标。
类别 D:包分析(BUN)
| 检查 ID | 检查项 | 阈值 | 方法 |
|---|---|---|---|
| BUN-01 | 主 JS 包大小 | ≤250KB(gzip 压缩后) | 检查主包的传输大小 |
| BUN-02 | JS 总大小 | ≤500KB(gzip 压缩后) | 汇总所有 JS 的传输大小 |
| BUN-03 | CSS 总大小 | ≤100KB(gzip 压缩后) | 汇总所有 CSS 的传输大小 |
| BUN-04 | 已实现代码拆分 | 最佳实践 | 检查是否存在多个 JS 分块 |
| BUN-05 | 无重复依赖项 | 包效率 | 分析分块内容中的重复项 |
| BUN-06 | Tree shaking 有效 | 包效率 | 检查已知的大型未使用导出项 |
| BUN-07 | 生产环境未暴露源映射 | 安全性/性能 | 检查 .map 文件是否可访问 |
| BUN-08 | 第三方 JS 预算 | ≤JS 总量的 30% | 计算第三方与第一方的比例 |
浏览器验证: 使用 Performance API 测量传输大小。检查源映射 URL。分析脚本的域名来源。
类别 E:运行时性能(RUN)
| 检查 ID | 检查项 | 阈值 | 方法 |
|---|---|---|---|
| RUN-01 | 交互期间没有长任务 | >50ms = 长任务 | 使用 PerformanceObserver 监测长任务 |
| RUN-02 | 滚动性能流畅 | 60fps | 滚动页面,测量帧丢失情况 |
| RUN-03 | 动画性能 | 60fps | 触发动画,测量卡顿情况 |
| RUN-04 | 内存使用稳定(无泄漏) | 无增长趋势 | 随时间测量 performance.memory |
| RUN-05 | DOM 节点数量不过多 | ≤1500 个节点 | 统计 document.querySelectorAll('*').length |
| RUN-06 | 没有布局抖动 | 0 次强制回流 | 监测强制样式重新计算 |
| RUN-07 | 事件监听器效率高 | 监听器不过多 | 检查未进行节流的滚动/调整大小监听器 |
浏览器验证: 使用 JavaScript 测量性能指标,同时浏览并与应用交互。
第 4 阶段:报告
生成结构化报告,并保存到 shiplight/reports/performance-review-{date}.md:
# Performance Review Report
**Date:** {date}
**URL:** {url}
**Pages tested:** {list}
**Conditions:** Desktop + Mobile (simulated Moto G4 / Slow 4G)
## Overall Score: {X}/10 | Confidence: {X}%
## Core Web Vitals Summary
| Metric | Desktop | Mobile | Status |
|--------|---------|--------|--------|
| LCP | 1.8s | 3.2s | ⚠️ Mobile needs work |
| INP | 95ms | 180ms | ✅ Good |
| CLS | 0.05 | 0.15 | ⚠️ Mobile needs work |
## Score Breakdown
| Category | Score | Findings |
|----------|-------|----------|
| Core Web Vitals (CWV) | 6/10 | 1 high, 1 medium |
| Page Load (LOAD) | 7/10 | 1 high |
| Resources (RES) | 5/10 | 2 high, 2 medium |
| Bundle (BUN) | 6/10 | 1 high, 1 medium |
| Runtime (RUN) | 8/10 | 1 medium |
## Resource Waterfall
(Top 10 slowest resources with load times)
## Bundle Breakdown
| Category | Size (gzipped) | Budget | Status |
|----------|---------------|--------|--------|
| First-party JS | 180KB | 250KB | ✅ |
| Third-party JS | 220KB | 150KB | ❌ Over budget |
| CSS | 45KB | 100KB | ✅ |
| Images | 1.2MB | 1.5MB | ✅ |
| Fonts | 85KB | 100KB | ✅ |
## Findings
(structured findings with metrics and evidence)
置信度评分
- 90-100%:在浏览器中测得具体数值(例如,LCP:3.2s)
- 70-89%:根据资源分析得出(例如,检测到未优化的图片)
- 50-69%:代码层面的模式(例如,未找到延迟加载属性)
- 低于 50%:不要报告
第 5 阶段:修复
1. 修复指南(示例)
#### RES-01: Images not using modern formats
**Impact:** ~40% larger images than necessary, adds ~500KB to page weight
**Current:** 8 PNG images totaling 1.2MB
**Fix:** Convert to WebP with fallback:
- Use `<picture>` with WebP source and PNG fallback
- Or use Next.js `<Image>` / `sharp` for automatic format negotiation
- Expected savings: ~480KB (40% reduction)
**Priority files:**
- /images/hero.png (320KB → ~190KB as WebP)
- /images/features.png (280KB → ~165KB as WebP)
2. YAML 回归测试
- name: cwv-01-lcp-under-threshold
description: Verify Largest Contentful Paint is under 2.5 seconds
severity: high
standard: Core-Web-Vitals-LCP
steps:
- description: Set up an LCP observer before navigation
js: |
// Set up LCP observer before navigation
await page.evaluateOnNewDocument(() => {
window.__lcp = 0;
new PerformanceObserver((list) => {
const entries = list.getEntries();
window.__lcp = entries[entries.length - 1].startTime;
}).observe({ type: 'largest-contentful-paint', buffered: true });
});
- URL: /
- WAIT_UNTIL: Page has fully loaded including all images and content
timeout_seconds: 30
- description: Assert Largest Contentful Paint is under 2.5s
js: |
const lcp = await page.evaluate(() => window.__lcp);
if (lcp > 2500) {
throw new Error(`LCP is ${lcp}ms, exceeds 2500ms threshold`);
}
console.log(`LCP: ${lcp}ms (threshold: 2500ms)`);
- VERIFY: Page loaded with Largest Contentful Paint under 2.5 seconds
将所有 YAML 测试保存到 shiplight/tests/performance-review.test.yaml。
深度级别
--quick:仅检查主页上的 Core Web Vitals。约 2 分钟。- 默认:检查关键页面上的所有类别,涵盖桌面端和移动端。约 8-12 分钟。
--thorough:检查所有类别 + 扩展页面 + 多次运行以提升统计置信度 + 运行时性能分析。约 20-30 分钟。
提示
- 在生产环境(或类生产构建)中测量——开发模式下的性能会产生误导
- 多次运行——性能测量结果会有差异;应关注趋势,而非单个数据点
- 移动端模拟会暴露桌面端无法发现的问题——务必同时测试两者
- 使用
performance.getEntries()——它是浏览器中最丰富的性能数据来源 - 优先关注 Core Web Vitals——这是 Google 用于排名的指标
- 使用
close_session结束会话,并使用generate_html_report提供证据