Lighthouse 性能审计
问题
什么是 Lighthouse?如何使用 Lighthouse 进行性能审计?如何在 CI/CD 中集成 Lighthouse?
什么是 Lighthouse? Google 开源的网页质量审计工具,是前端性能体检的事实标准:
- 常用核心类别包括 Performance、Accessibility、Best Practices 和 SEO。类别与审计项会随 Lighthouse 版本调整,PWA 不应再背成固定的“第五个顶级评分”。
- Performance 评分使用实验室数据:网络、CPU、视口和缓存条件由运行配置决定,并不固定等于“Slow 4G + 4x”,也不等于真实用户数据(RUM)。
- 90+ 是绿色优秀、50-89 橙色待改进、0-49 红色差。Performance 是最常被关注的,权重最高。
如何使用 Lighthouse 进行性能审计? 四种使用方式按场景选:
- Chrome DevTools Lighthouse 面板:最快,F12 → Lighthouse → 跑一次拿报告,本地开发常用。
- PageSpeed Insights 网页版:拿真实用户的 CrUX 数据 + Lighthouse 实验室数据,更接近线上情况。
- CLI:
lighthouse https://xxx --output html适合脚本化。 - Node.js API:嵌到自动化流程里。
- 看报告重点:官方评分页当前仍展示 Lighthouse 10 的
TBT 30% + LCP 25% + CLS 25% + FCP 10% + SI 10%模型,但权重会随版本变化。不要只追总分,要看具体指标、Insights 和 trace 证据。
如何在 CI/CD 中集成 Lighthouse?
用 Lighthouse CI(@lhci/cli)做性能门禁:
- 在 GitHub Actions/GitLab CI 里跑
lhci autorun,每次 PR 自动跑 Lighthouse 几次取中位数。 - 在
lighthouserc.js里配assertions——比如'first-contentful-paint': ['error', { maxNumericValue: 2000 }],超阈值直接 PR fail。 - 报告上传到 LHCI Server 或 GitHub Pages 留档,能看历史趋势。
- 配合性能预算用——例如中端移动设备、冷缓存和模拟 4G 的关键营销页,可以先以首屏压缩后 JS 200KB、LCP 2.5s 作为初始预算,再用真实用户基线校准,而不是把该数字套给所有页面。
答案
Lighthouse 是 Google 开源的网页质量审计工具,用于测量和改进网页的性能、可访问性、最佳实践、SEO 等维度。它适合可重复的实验室审计和 CI 回归,不是线上 RUM 的替代品。
Lighthouse 简介
审计维度
Lighthouse 的默认类别、审计 ID、Insights 和评分权重都可能变化。CI 应锁定 Lighthouse/LHCI 版本并记录版本号;升级时先在基准页面上比较报告差异,再更新门禁。
评分计算
| 分数范围 | 评级 | 颜色 |
|---|---|---|
| 90-100 | 优秀 | 🟢 绿色 |
| 50-89 | 需改进 | 🟠 橙色 |
| 0-49 | 差 | 🔴 红色 |
Performance 评分权重
下表是官方评分文档当前展示的 Lighthouse 10 权重,用于理解分数构成,不应假设未来版本永远不变。
| 指标 | 权重 | 说明 |
|---|---|---|
| TBT (Total Blocking Time) | 30% | 阻塞时间 |
| LCP (Largest Contentful Paint) | 25% | 最大内容绘制 |
| CLS (Cumulative Layout Shift) | 25% | 累积布局偏移 |
| FCP (First Contentful Paint) | 10% | 首次内容绘制 |
| SI (Speed Index) | 10% | 速度指数 |
使用方式
1. Chrome DevTools
1. 打开 Chrome DevTools (F12)
2. 切换到 Lighthouse 面板
3. 选择审计类别和设备类型
4. 点击 "Analyze page load"
Lighthouse 适合快速体检。需要定位主线程、网络瀑布、LCP/INP 根因时,应转到 Performance 面板录制真实用户路径,并查看其中的 Insights;新版 Lighthouse 的性能建议也逐步采用相同的 Insights 模型。
2. Chrome 扩展(可选)
1. 安装 Lighthouse Chrome 扩展
2. 点击扩展图标
3. 选择审计选项
4. 生成报告
3. 命令行工具
- npm
- Yarn
- pnpm
- Bun
npm install -g lighthouse
yarn global add lighthouse
pnpm add -g lighthouse
bun add --global lighthouse
# 基本使用
lighthouse https://example.com
# 指定输出格式
lighthouse https://example.com --output html --output-path ./report.html
# 指定设备
lighthouse https://example.com --preset=desktop
# 只审计性能
lighthouse https://example.com --only-categories=performance
# JSON 输出
lighthouse https://example.com --output json --output-path ./report.json
4. Node.js API
import lighthouse from 'lighthouse';
import chromeLauncher from 'chrome-launcher';
async function runLighthouse(url: string) {
// 启动 Chrome
const chrome = await chromeLauncher.launch({ chromeFlags: ['--headless'] });
// 运行 Lighthouse
const options = {
logLevel: 'info' as const,
output: 'json' as const,
onlyCategories: ['performance'],
port: chrome.port,
};
const result = await lighthouse(url, options);
// 关闭 Chrome
await chrome.kill();
return result;
}
// 使用
const result = await runLighthouse('https://example.com');
console.log('Performance Score:', result?.lhr.categories.performance.score);
核心性能指标
Performance 指标详解
| 指标 | 全称 | 良好 | 需改进 | 差 |
|---|---|---|---|---|
| FCP | First Contentful Paint | <1.8s | 1.8-3s | >3s |
| LCP | Largest Contentful Paint | <2.5s | 2.5-4s | >4s |
| TBT | Total Blocking Time | <200ms | 200-600ms | >600ms |
| CLS | Cumulative Layout Shift | <0.1 | 0.1-0.25 | >0.25 |
| SI | Speed Index | <3.4s | 3.4-5.8s | >5.8s |
指标含义
// FCP - 首次内容绘制
// 浏览器首次渲染任何文本、图片、非空白 canvas 或 SVG 的时间
// LCP - 最大内容绘制
// 视口内最大的图片、视频或文本块完成渲染的时间
// 常见 LCP 元素:<img>、<video>、背景图、大块文本
// TBT - 总阻塞时间(实验室指标)
// 加载期从 FCP 到达到可交互状态之间,长任务超过 50ms 部分的总和
// 长任务 = 执行时间 > 50ms 的任务
// CLS - 累积布局偏移
// 衡量视觉稳定性,页面加载期间意外布局移动的总和
// SI - 速度指数
// 页面内容可见的填充速度,越低越好
从 Lighthouse Insights 定位问题
新版报告不应只按旧的 Opportunities 列表逐项“刷分”。更有效的做法是把 Insight 当作调查入口,再回到 Performance trace 验证:
| Insight 方向 | 回答的问题 | 下一步证据 |
|---|---|---|
| LCP breakdown/discovery | LCP 慢在 TTFB、发现、下载还是渲染? | Network 瀑布、LCP 元素、优先级 |
| Render-blocking requests | 哪些资源阻塞首屏? | 请求开始时间、CSS 使用率、依赖链 |
| Third parties | 哪些第三方消耗下载和主线程? | 域名、CPU 时间、请求阻塞实验 |
| Legacy/duplicated JavaScript | 是否下发重复依赖或不必要的旧语法? | bundle 分析、source map、浏览器目标 |
| Layout shifts | 哪些元素造成意外偏移? | Layout Shift cluster 与 affected nodes |
| INP breakdown | 交互慢在输入、处理还是呈现? | 录制具体交互、事件回调和渲染阶段 |
一条 Insight 只是“可能有收益”的假设。先看它是否位于关键路径,再做一次小改动,用相同环境重复测试并检查现场数据,避免为了消除报告提示反而增加 preload、缓存复杂度或客户端代码。
常见优化建议
1. 减少渲染阻塞资源
<!-- ❌ 阻塞渲染 -->
<link rel="stylesheet" href="styles.css">
<script src="app.js"></script>
<!-- ✅ 优化后 -->
<!-- 关键 CSS 内联 -->
<style>/* 关键样式 */</style>
<!-- 非关键 CSS 延迟加载 -->
<link rel="preload" href="non-critical.css" as="style" onload="this.rel='stylesheet'">
<!-- JS 异步加载 -->
<script src="app.js" defer></script>
2. 减少未使用的 JavaScript
// 使用代码分割
const LazyComponent = lazy(() => import('./LazyComponent'));
// 使用 Tree Shaking
// 只导入需要的函数
import { debounce } from 'lodash-es';
// 而不是
import _ from 'lodash';
3. 服务静态资源使用高效缓存策略
# nginx 配置
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff2)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
4. 延迟加载屏幕外图片
<img src="image.jpg" loading="lazy" alt="描述">
5. 避免巨大的网络负载
// 使用现代图片格式
<picture>
<source srcset="image.avif" type="image/avif">
<source srcset="image.webp" type="image/webp">
<img src="image.jpg" alt="描述">
</picture>
// 压缩资源
// Brotli 压缩(比 gzip 小 15-20%)
CI/CD 集成
GitHub Actions
# .github/workflows/lighthouse.yml
name: Lighthouse CI
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
lighthouse:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Install dependencies
run: npm ci
- name: Build
run: npm run build
- name: Start server
run: npm run preview &
- name: Wait for server
run: sleep 10
- name: Run Lighthouse
uses: treosh/lighthouse-ci-action@v11
with:
urls: |
http://localhost:3000
http://localhost:3000/about
uploadArtifacts: true
temporaryPublicStorage: true
configPath: './lighthouserc.json'
Lighthouse CI 配置
下面是一组适合“中端移动设备、冷缓存、关键公开页面”的初始示例。真实项目应先跑基线,再根据页面类型调整;登录页、后台和营销页不应共用一套阈值。
{
"ci": {
"collect": {
"url": ["http://localhost:3000"],
"numberOfRuns": 3
},
"assert": {
"assertions": {
"categories:performance": ["warn", { "minScore": 0.9 }],
"categories:accessibility": ["warn", { "minScore": 0.9 }],
"categories:best-practices": ["warn", { "minScore": 0.9 }],
"categories:seo": ["warn", { "minScore": 0.9 }],
"first-contentful-paint": ["error", { "maxNumericValue": 2000 }],
"largest-contentful-paint": ["error", { "maxNumericValue": 2500 }],
"cumulative-layout-shift": ["error", { "maxNumericValue": 0.1 }],
"total-blocking-time": ["error", { "maxNumericValue": 300 }]
}
},
"upload": {
"target": "temporary-public-storage"
}
}
}
总分受多个指标和运行噪声影响,更适合作为趋势或 warning;稳定、可解释的单项指标和资源预算才适合逐步升级为 error。temporary-public-storage 可能公开报告,不要用于包含登录态、内部 URL 或敏感页面的 CI。
自动化脚本
// lighthouse-ci.ts
import lighthouse from 'lighthouse';
import chromeLauncher from 'chrome-launcher';
interface AuditResult {
url: string;
scores: Record<string, number>;
passed: boolean;
}
async function auditUrl(url: string): Promise<AuditResult> {
const chrome = await chromeLauncher.launch({
chromeFlags: ['--headless', '--no-sandbox']
});
const result = await lighthouse(url, {
port: chrome.port,
output: 'json',
});
await chrome.kill();
const lhr = result?.lhr;
if (!lhr) throw new Error('Lighthouse failed');
const scores: Record<string, number> = {};
Object.entries(lhr.categories).forEach(([key, category]) => {
scores[key] = Math.round((category.score || 0) * 100);
});
// 检查是否通过阈值
const passed = scores.performance >= 90 &&
scores.accessibility >= 90 &&
scores['best-practices'] >= 90;
return { url, scores, passed };
}
async function runAudit() {
const urls = [
'http://localhost:3000',
'http://localhost:3000/about',
];
const results = await Promise.all(urls.map(auditUrl));
console.table(results.map(r => ({
URL: r.url,
Performance: r.scores.performance,
Accessibility: r.scores.accessibility,
'Best Practices': r.scores['best-practices'],
SEO: r.scores.seo,
Passed: r.passed ? '✅' : '❌',
})));
const allPassed = results.every(r => r.passed);
process.exit(allPassed ? 0 : 1);
}
runAudit();
性能监控集成
PageSpeed Insights API
async function getPageSpeedInsights(url: string) {
const apiKey = process.env.PSI_API_KEY;
const endpoint = `https://www.googleapis.com/pagespeedonline/v5/runPagespeed`;
const params = new URLSearchParams({
url,
key: apiKey || '',
strategy: 'mobile',
category: 'PERFORMANCE',
category: 'ACCESSIBILITY',
});
const response = await fetch(`${endpoint}?${params}`);
const data = await response.json();
return {
performance: data.lighthouseResult.categories.performance.score * 100,
fcp: data.lighthouseResult.audits['first-contentful-paint'].displayValue,
lcp: data.lighthouseResult.audits['largest-contentful-paint'].displayValue,
cls: data.lighthouseResult.audits['cumulative-layout-shift'].displayValue,
};
}
定时监控
// 使用 cron 定时运行
import cron from 'node-cron';
cron.schedule('0 */6 * * *', async () => {
// 每 6 小时运行一次
const result = await auditUrl('https://mysite.com');
if (result.scores.performance < 80) {
// 发送告警
await sendAlert({
type: 'performance_degradation',
score: result.scores.performance,
});
}
// 保存历史数据
await saveToDatabase(result);
});
报告分析
常见问题诊断
| 问题 | 可能原因 | 解决方案 |
|---|---|---|
| LCP 高 | 图片未优化 | WebP/懒加载/preload |
| FCP 高 | 渲染阻塞资源 | async/defer/内联关键CSS |
| TBT 高 | 长任务 | 代码分割/Web Worker |
| CLS 高 | 图片无尺寸 | 设置 width/height |
| 低分 | 第三方脚本 | 延迟加载/async |
追踪优化效果
// 记录优化前后的分数变化
interface PerformanceRecord {
date: Date;
scores: {
performance: number;
accessibility: number;
bestPractices: number;
seo: number;
};
metrics: {
fcp: number;
lcp: number;
tbt: number;
cls: number;
};
}
// 生成趋势图
function generateTrendReport(records: PerformanceRecord[]) {
const trend = records.map(r => ({
date: r.date.toISOString().split('T')[0],
performance: r.scores.performance,
lcp: r.metrics.lcp,
}));
console.table(trend);
}
常见面试问题
Q1: Lighthouse 评估哪些维度?
答案:
| 维度 | 说明 | 核心指标 |
|---|---|---|
| Performance | 页面性能 | FCP、LCP、TBT、CLS、SI |
| Accessibility | 可访问性 | 颜色对比度、ARIA 标签 |
| Best Practices | 最佳实践 | HTTPS、无控制台错误 |
| SEO | 搜索优化 | meta 标签、语义化 |
类别和审计项会随版本调整。PWA 的 manifest、离线和可安装能力仍然重要,但不应再把 PWA 背成所有当前版本固定存在的第五个顶级评分。
Q2: 如何提高 Lighthouse Performance 分数?
答案:
Q3: Lighthouse 分数不稳定怎么办?
答案:
- 多次运行并使用中位运行结果
# 在 lighthouserc.json 中设置 collect.numberOfRuns,例如 5
lhci autorun
- 使用稳定的测试环境
// 使用 Docker 隔离环境
// 关闭浏览器扩展
// 使用有线网络
- 排除网络因素
// 使用 throttling 模拟一致的网络条件
const options = {
throttling: {
cpuSlowdownMultiplier: 4,
downloadThroughputKbps: 1638,
uploadThroughputKbps: 675,
rttMs: 150,
},
};
-
锁定版本和页面状态:锁定 Chrome、Lighthouse、Node、字体、测试数据和登录态;避免 A/B 实验、广告和后台任务污染结果。
-
不要只看总分:同时保存 LCP/TBT/CLS、请求数量和传输体积,使用绝对阈值加相对回归,避免一次波动误伤 PR。
Q4: 如何在 CI/CD 中使用 Lighthouse?
答案:
# GitHub Actions 示例
- name: Lighthouse CI
uses: treosh/lighthouse-ci-action@v11
with:
urls: http://localhost:3000
configPath: ./lighthouserc.json
# 阈值检查
assertions:
categories:performance: ["error", { "minScore": 0.9 }]
largest-contentful-paint: ["error", { "maxNumericValue": 2500 }]
Q5: Lighthouse 和真实用户数据的区别?
答案:
| 特性 | Lighthouse (Lab Data) | RUM (Field Data) |
|---|---|---|
| 数据来源 | 模拟环境 | 真实用户 |
| 一致性 | 高 | 取决于用户环境 |
| 实时性 | 即时 | 需要收集 |
| 网络条件 | 固定模拟 | 实际网络 |
| 用途 | 开发调试 | 监控真实性能 |
最佳实践:两者结合使用
- Lighthouse:开发阶段快速反馈
- RUM (Core Web Vitals):监控真实用户体验