lighthouse-ci-integrator
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseLighthouse CI Integrator
Lighthouse CI 集成工具
Sets up Lighthouse CI to automatically test performance, accessibility, SEO, and best practices in your CI/CD pipeline with budget enforcement and trend tracking.
在CI/CD流水线中搭建Lighthouse CI,实现性能、无障碍访问、SEO及最佳实践的自动化测试,并支持性能预算管控与趋势追踪。
When to Use
适用场景
- "Setup Lighthouse CI"
- "Add performance testing to CI/CD"
- "Monitor Core Web Vitals"
- "Prevent performance regressions"
- "Track Lighthouse scores"
- "Setup performance budgets"
- 「搭建Lighthouse CI」
- 「为CI/CD添加性能测试」
- 「监控Core Web Vitals」
- 「防止性能回归」
- 「追踪Lighthouse评分」
- 「设置性能预算」
Instructions
操作步骤
1. Install Lighthouse CI
1. 安装Lighthouse CI
bash
npm install --save-dev @lhci/clibash
npm install --save-dev @lhci/clior
or
yarn add --dev @lhci/cli
undefinedyarn add --dev @lhci/cli
undefined2. Create Configuration File
2. 创建配置文件
lighthouserc.js:
javascript
module.exports = {
ci: {
collect: {
// URLs to test
url: [
'http://test-frontend:3000/',
'http://test-frontend:3000/about',
'http://test-frontend:3000/products',
],
// Number of runs per URL
numberOfRuns: 3,
// Start server before collecting
startServerCommand: 'npm run serve',
startServerReadyPattern: 'Server listening',
// Or use static directory
staticDistDir: './dist',
// Settings
settings: {
preset: 'desktop', // or 'mobile'
// Throttling
throttling: {
rttMs: 40,
throughputKbps: 10240,
cpuSlowdownMultiplier: 1,
},
// Screen emulation
screenEmulation: {
mobile: false,
width: 1350,
height: 940,
deviceScaleFactor: 1,
disabled: false,
},
},
},
upload: {
target: 'temporary-public-storage',
// Or use LHCI server
// target: 'lhci',
// serverBaseUrl: 'https://your-lhci-server.com',
// token: process.env.LHCI_TOKEN,
},
assert: {
preset: 'lighthouse:recommended',
assertions: {
// Performance
'categories:performance': ['error', { minScore: 0.9 }],
'first-contentful-paint': ['warn', { maxNumericValue: 2000 }],
'largest-contentful-paint': ['error', { maxNumericValue: 2500 }],
'cumulative-layout-shift': ['error', { maxNumericValue: 0.1 }],
'total-blocking-time': ['warn', { maxNumericValue: 300 }],
'speed-index': ['warn', { maxNumericValue: 3000 }],
'interactive': ['warn', { maxNumericValue: 3500 }],
// Accessibility
'categories:accessibility': ['error', { minScore: 0.95 }],
// Best Practices
'categories:best-practices': ['error', { minScore: 0.9 }],
// SEO
'categories:seo': ['warn', { minScore: 0.9 }],
// Resource budgets
'resource-summary:script:size': ['error', { maxNumericValue: 500000 }],
'resource-summary:stylesheet:size': ['warn', { maxNumericValue: 100000 }],
'resource-summary:image:size': ['warn', { maxNumericValue: 1000000 }],
'resource-summary:font:size': ['warn', { maxNumericValue: 100000 }],
'total-byte-weight': ['warn', { maxNumericValue: 2000000 }],
// Other metrics
'uses-http2': 'error',
'uses-webp-images': 'warn',
'offscreen-images': 'warn',
'unused-css-rules': 'warn',
'unused-javascript': 'warn',
'modern-image-formats': 'warn',
'uses-optimized-images': 'warn',
'uses-text-compression': 'error',
'uses-responsive-images': 'warn',
},
},
},
};Mobile Configuration:
javascript
// lighthouserc.mobile.js
module.exports = {
ci: {
collect: {
url: ['http://test-frontend:3000/'],
numberOfRuns: 3,
settings: {
preset: 'mobile',
throttling: {
rttMs: 150,
throughputKbps: 1638,
cpuSlowdownMultiplier: 4,
},
screenEmulation: {
mobile: true,
width: 412,
height: 823,
deviceScaleFactor: 2.625,
disabled: false,
},
formFactor: 'mobile',
},
},
assert: {
assertions: {
'categories:performance': ['error', { minScore: 0.85 }],
'first-contentful-paint': ['error', { maxNumericValue: 2500 }],
'largest-contentful-paint': ['error', { maxNumericValue: 4000 }],
},
},
},
};lighthouserc.js:
javascript
module.exports = {
ci: {
collect: {
// URLs to test
url: [
'http://test-frontend:3000/',
'http://test-frontend:3000/about',
'http://test-frontend:3000/products',
],
// Number of runs per URL
numberOfRuns: 3,
// Start server before collecting
startServerCommand: 'npm run serve',
startServerReadyPattern: 'Server listening',
// Or use static directory
staticDistDir: './dist',
// Settings
settings: {
preset: 'desktop', // or 'mobile'
// Throttling
throttling: {
rttMs: 40,
throughputKbps: 10240,
cpuSlowdownMultiplier: 1,
},
// Screen emulation
screenEmulation: {
mobile: false,
width: 1350,
height: 940,
deviceScaleFactor: 1,
disabled: false,
},
},
},
upload: {
target: 'temporary-public-storage',
// Or use LHCI server
// target: 'lhci',
// serverBaseUrl: 'https://your-lhci-server.com',
// token: process.env.LHCI_TOKEN,
},
assert: {
preset: 'lighthouse:recommended',
assertions: {
// Performance
'categories:performance': ['error', { minScore: 0.9 }],
'first-contentful-paint': ['warn', { maxNumericValue: 2000 }],
'largest-contentful-paint': ['error', { maxNumericValue: 2500 }],
'cumulative-layout-shift': ['error', { maxNumericValue: 0.1 }],
'total-blocking-time': ['warn', { maxNumericValue: 300 }],
'speed-index': ['warn', { maxNumericValue: 3000 }],
'interactive': ['warn', { maxNumericValue: 3500 }],
// Accessibility
'categories:accessibility': ['error', { minScore: 0.95 }],
// Best Practices
'categories:best-practices': ['error', { minScore: 0.9 }],
// SEO
'categories:seo': ['warn', { minScore: 0.9 }],
// Resource budgets
'resource-summary:script:size': ['error', { maxNumericValue: 500000 }],
'resource-summary:stylesheet:size': ['warn', { maxNumericValue: 100000 }],
'resource-summary:image:size': ['warn', { maxNumericValue: 1000000 }],
'resource-summary:font:size': ['warn', { maxNumericValue: 100000 }],
'total-byte-weight': ['warn', { maxNumericValue: 2000000 }],
// Other metrics
'uses-http2': 'error',
'uses-webp-images': 'warn',
'offscreen-images': 'warn',
'unused-css-rules': 'warn',
'unused-javascript': 'warn',
'modern-image-formats': 'warn',
'uses-optimized-images': 'warn',
'uses-text-compression': 'error',
'uses-responsive-images': 'warn',
},
},
},
};移动端配置:
javascript
// lighthouserc.mobile.js
module.exports = {
ci: {
collect: {
url: ['http://test-frontend:3000/'],
numberOfRuns: 3,
settings: {
preset: 'mobile',
throttling: {
rttMs: 150,
throughputKbps: 1638,
cpuSlowdownMultiplier: 4,
},
screenEmulation: {
mobile: true,
width: 412,
height: 823,
deviceScaleFactor: 2.625,
disabled: false,
},
formFactor: 'mobile',
},
},
assert: {
assertions: {
'categories:performance': ['error', { minScore: 0.85 }],
'first-contentful-paint': ['error', { maxNumericValue: 2500 }],
'largest-contentful-paint': ['error', { maxNumericValue: 4000 }],
},
},
},
};3. Add npm Scripts
3. 添加npm脚本
package.json:
json
{
"scripts": {
"build": "next build",
"serve": "next start",
"lhci:collect": "lhci collect",
"lhci:assert": "lhci assert",
"lhci:upload": "lhci upload",
"lhci:autorun": "lhci autorun",
"lhci:mobile": "lhci autorun --config=lighthouserc.mobile.js",
"lhci:desktop": "lhci autorun --config=lighthouserc.js"
}
}package.json:
json
{
"scripts": {
"build": "next build",
"serve": "next start",
"lhci:collect": "lhci collect",
"lhci:assert": "lhci assert",
"lhci:upload": "lhci upload",
"lhci:autorun": "lhci autorun",
"lhci:mobile": "lhci autorun --config=lighthouserc.mobile.js",
"lhci:desktop": "lhci autorun --config=lighthouserc.js"
}
}4. GitHub Actions Integration
4. GitHub Actions集成
.github/workflows/lighthouse-ci.yml:
yaml
name: Lighthouse CI
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
lighthouse:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
ref: ${{ github.event.pull_request.head.sha }}
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: '18'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Build application
run: npm run build
- name: Run Lighthouse CI (Desktop)
run: npm run lhci:desktop
env:
LHCI_GITHUB_APP_TOKEN: ${{ secrets.LHCI_GITHUB_APP_TOKEN }}
- name: Run Lighthouse CI (Mobile)
run: npm run lhci:mobile
env:
LHCI_GITHUB_APP_TOKEN: ${{ secrets.LHCI_GITHUB_APP_TOKEN }}
- name: Upload Lighthouse results
uses: actions/upload-artifact@v3
if: always()
with:
name: lighthouse-results
path: .lighthouseciWith deployment preview (Vercel/Netlify):
yaml
name: Lighthouse CI with Preview
on:
pull_request:
branches: [main]
jobs:
lighthouse:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Wait for Vercel Preview
uses: patrickedqvist/wait-for-vercel-preview@v1.2.0
id: wait-for-vercel
with:
token: ${{ secrets.GITHUB_TOKEN }}
max_timeout: 300
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: '18'
- name: Install Lighthouse CI
run: npm install -g @lhci/cli
- name: Run Lighthouse CI
run: |
lhci autorun --url=${{ steps.wait-for-vercel.outputs.url }}
env:
LHCI_GITHUB_APP_TOKEN: ${{ secrets.LHCI_GITHUB_APP_TOKEN }}.github/workflows/lighthouse-ci.yml:
yaml
name: Lighthouse CI
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
lighthouse:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
ref: ${{ github.event.pull_request.head.sha }}
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: '18'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Build application
run: npm run build
- name: Run Lighthouse CI (Desktop)
run: npm run lhci:desktop
env:
LHCI_GITHUB_APP_TOKEN: ${{ secrets.LHCI_GITHUB_APP_TOKEN }}
- name: Run Lighthouse CI (Mobile)
run: npm run lhci:mobile
env:
LHCI_GITHUB_APP_TOKEN: ${{ secrets.LHCI_GITHUB_APP_TOKEN }}
- name: Upload Lighthouse results
uses: actions/upload-artifact@v3
if: always()
with:
name: lighthouse-results
path: .lighthouseci结合部署预览(Vercel/Netlify):
yaml
name: Lighthouse CI with Preview
on:
pull_request:
branches: [main]
jobs:
lighthouse:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Wait for Vercel Preview
uses: patrickedqvist/wait-for-vercel-preview@v1.2.0
id: wait-for-vercel
with:
token: ${{ secrets.GITHUB_TOKEN }}
max_timeout: 300
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: '18'
- name: Install Lighthouse CI
run: npm install -g @lhci/cli
- name: Run Lighthouse CI
run: |
lhci autorun --url=${{ steps.wait-for-vercel.outputs.url }}
env:
LHCI_GITHUB_APP_TOKEN: ${{ secrets.LHCI_GITHUB_APP_TOKEN }}5. GitLab CI Integration
5. GitLab CI集成
.gitlab-ci.yml:
yaml
lighthouse:
stage: test
image: node:18
before_script:
- npm ci
script:
- npm run build
- npm run lhci:autorun
artifacts:
paths:
- .lighthouseci
expire_in: 1 week
only:
- merge_requests
- main.gitlab-ci.yml:
yaml
lighthouse:
stage: test
image: node:18
before_script:
- npm ci
script:
- npm run build
- npm run lhci:autorun
artifacts:
paths:
- .lighthouseci
expire_in: 1 week
only:
- merge_requests
- main6. Setup LHCI Server (Optional)
6. 搭建LHCI服务器(可选)
Docker Compose for LHCI Server:
yaml
undefinedLHCI服务器Docker Compose配置:
yaml
undefineddocker-compose.lhci.yml
docker-compose.lhci.yml
version: '3.8'
services:
lhci-server:
image: patrickhulce/lhci-server:latest
ports:
- '9001:9001'
environment:
LHCI_STORAGE_METHOD: sql
LHCI_STORAGE_SQL_DIALECT: postgres
LHCI_STORAGE_SQL_DATABASE: lighthouse
LHCI_STORAGE_SQL_USERNAME: postgres
LHCI_STORAGE_SQL_PASSWORD: postgres
LHCI_STORAGE_SQL_HOST: postgres
depends_on:
- postgres
postgres:
image: postgres:14
environment:
POSTGRES_DB: lighthouse
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
volumes:
- lhci-postgres-data:/var/lib/postgresql/data
volumes:
lhci-postgres-data:
**Start server:**
```bash
docker-compose -f docker-compose.lhci.yml up -dCreate project:
bash
lhci wizardversion: '3.8'
services:
lhci-server:
image: patrickhulce/lhci-server:latest
ports:
- '9001:9001'
environment:
LHCI_STORAGE_METHOD: sql
LHCI_STORAGE_SQL_DIALECT: postgres
LHCI_STORAGE_SQL_DATABASE: lighthouse
LHCI_STORAGE_SQL_USERNAME: postgres
LHCI_STORAGE_SQL_PASSWORD: postgres
LHCI_STORAGE_SQL_HOST: postgres
depends_on:
- postgres
postgres:
image: postgres:14
environment:
POSTGRES_DB: lighthouse
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
volumes:
- lhci-postgres-data:/var/lib/postgresql/data
volumes:
lhci-postgres-data:
**启动服务器:**
```bash
docker-compose -f docker-compose.lhci.yml up -d创建项目:
bash
lhci wizardFollow prompts to create project and get token
Follow prompts to create project and get token
undefinedundefined7. Budget.json (Alternative Format)
7. Budget.json(替代格式)
budget.json:
json
[
{
"path": "/*",
"timings": [
{
"metric": "interactive",
"budget": 3500
},
{
"metric": "first-meaningful-paint",
"budget": 2000
}
],
"resourceSizes": [
{
"resourceType": "script",
"budget": 300
},
{
"resourceType": "image",
"budget": 500
},
{
"resourceType": "stylesheet",
"budget": 100
},
{
"resourceType": "font",
"budget": 100
},
{
"resourceType": "total",
"budget": 1000
}
],
"resourceCounts": [
{
"resourceType": "third-party",
"budget": 10
}
]
}
]budget.json:
json
[
{
"path": "/*",
"timings": [
{
"metric": "interactive",
"budget": 3500
},
{
"metric": "first-meaningful-paint",
"budget": 2000
}
],
"resourceSizes": [
{
"resourceType": "script",
"budget": 300
},
{
"resourceType": "image",
"budget": 500
},
{
"resourceType": "stylesheet",
"budget": 100
},
{
"resourceType": "font",
"budget": 100
},
{
"resourceType": "total",
"budget": 1000
}
],
"resourceCounts": [
{
"resourceType": "third-party",
"budget": 10
}
]
}
]8. Custom Audits
8. 自定义审计
custom-audit.js:
javascript
// custom-audit.js
class CustomAudit extends Lighthouse.Audit {
static get meta() {
return {
id: 'custom-performance-check',
title: 'Custom Performance Check',
failureTitle: 'Custom performance check failed',
description: 'Validates custom performance requirements',
requiredArtifacts: ['devtoolsLogs', 'traces'],
};
}
static audit(artifacts, context) {
// Custom audit logic
const score = 1; // 0-1
return {
score,
numericValue: 100,
displayValue: '100ms',
};
}
}
module.exports = CustomAudit;Use in lighthouserc.js:
javascript
module.exports = {
ci: {
collect: {
settings: {
plugins: ['./custom-audit.js'],
},
},
},
};custom-audit.js:
javascript
// custom-audit.js
class CustomAudit extends Lighthouse.Audit {
static get meta() {
return {
id: 'custom-performance-check',
title: 'Custom Performance Check',
failureTitle: 'Custom performance check failed',
description: 'Validates custom performance requirements',
requiredArtifacts: ['devtoolsLogs', 'traces'],
};
}
static audit(artifacts, context) {
// Custom audit logic
const score = 1; // 0-1
return {
score,
numericValue: 100,
displayValue: '100ms',
};
}
}
module.exports = CustomAudit;在lighthouserc.js中使用:
javascript
module.exports = {
ci: {
collect: {
settings: {
plugins: ['./custom-audit.js'],
},
},
},
};9. PR Comments Integration
9. PR评论集成
Comment on PR with results:
yaml
undefined在PR中评论测试结果:
yaml
undefined.github/workflows/lighthouse-ci.yml
.github/workflows/lighthouse-ci.yml
- name: Comment PR with results uses: treosh/lighthouse-ci-action@v9 with: urls: | https://example.com https://example.com/about uploadArtifacts: true temporaryPublicStorage: true
undefined- name: Comment PR with results uses: treosh/lighthouse-ci-action@v9 with: urls: | https://example.com https://example.com/about uploadArtifacts: true temporaryPublicStorage: true
undefined10. Monitoring and Alerts
10. 监控与告警
Slack notifications:
yaml
undefinedSlack通知:
yaml
undefined.github/workflows/lighthouse-ci.yml
.github/workflows/lighthouse-ci.yml
- name: Notify Slack if: failure() uses: 8398a7/action-slack@v3 with: status: ${{ job.status }} text: 'Lighthouse CI failed! Performance regression detected.' webhook_url: ${{ secrets.SLACK_WEBHOOK }}
**Email notifications:**
```yaml
- name: Send email notification
if: failure()
uses: dawidd6/action-send-mail@v3
with:
server_address: smtp.gmail.com
server_port: 465
username: ${{ secrets.EMAIL_USERNAME }}
password: ${{ secrets.EMAIL_PASSWORD }}
subject: Lighthouse CI Failed
body: Performance regression detected in ${{ github.repository }}
to: team@example.com- name: Notify Slack if: failure() uses: 8398a7/action-slack@v3 with: status: ${{ job.status }} text: 'Lighthouse CI failed! Performance regression detected.' webhook_url: ${{ secrets.SLACK_WEBHOOK }}
**邮件通知:**
```yaml
- name: Send email notification
if: failure()
uses: dawidd6/action-send-mail@v3
with:
server_address: smtp.gmail.com
server_port: 465
username: ${{ secrets.EMAIL_USERNAME }}
password: ${{ secrets.EMAIL_PASSWORD }}
subject: Lighthouse CI Failed
body: Performance regression detected in ${{ github.repository }}
to: team@example.com11. Generate Reports
11. 生成报告
HTML Report:
bash
undefinedHTML报告:
bash
undefinedGenerate HTML report locally
Generate HTML report locally
lhci collect
lhci upload --target=filesystem --outputDir=./lighthouse-reports
lhci collect
lhci upload --target=filesystem --outputDir=./lighthouse-reports
Open report
Open report
open ./lighthouse-reports/index.html
**JSON Report:**
```javascript
// parse-lighthouse-results.js
const fs = require('fs');
const results = JSON.parse(fs.readFileSync('.lighthouseci/manifest.json'));
results.forEach(result => {
const report = JSON.parse(fs.readFileSync(result.jsonPath));
console.log('URL:', report.finalUrl);
console.log('Performance:', report.categories.performance.score * 100);
console.log('Accessibility:', report.categories.accessibility.score * 100);
console.log('Best Practices:', report.categories['best-practices'].score * 100);
console.log('SEO:', report.categories.seo.score * 100);
console.log('---');
});open ./lighthouse-reports/index.html
**JSON报告:**
```javascript
// parse-lighthouse-results.js
const fs = require('fs');
const results = JSON.parse(fs.readFileSync('.lighthouseci/manifest.json'));
results.forEach(result => {
const report = JSON.parse(fs.readFileSync(result.jsonPath));
console.log('URL:', report.finalUrl);
console.log('Performance:', report.categories.performance.score * 100);
console.log('Accessibility:', report.categories.accessibility.score * 100);
console.log('Best Practices:', report.categories['best-practices'].score * 100);
console.log('SEO:', report.categories.seo.score * 100);
console.log('---');
});12. Performance Budgets
12. 性能预算
Strict budgets:
javascript
assertions: {
// Core Web Vitals (strict)
'largest-contentful-paint': ['error', { maxNumericValue: 2500 }],
'cumulative-layout-shift': ['error', { maxNumericValue: 0.1 }],
'first-input-delay': ['error', { maxNumericValue: 100 }],
// Page weight
'total-byte-weight': ['error', { maxNumericValue: 1500000 }], // 1.5MB
'dom-size': ['warn', { maxNumericValue: 800 }],
// JavaScript
'bootup-time': ['warn', { maxNumericValue: 3500 }],
'mainthread-work-breakdown': ['warn', { maxNumericValue: 4000 }],
}严格预算:
javascript
assertions: {
// Core Web Vitals (strict)
'largest-contentful-paint': ['error', { maxNumericValue: 2500 }],
'cumulative-layout-shift': ['error', { maxNumericValue: 0.1 }],
'first-input-delay': ['error', { maxNumericValue: 100 }],
// Page weight
'total-byte-weight': ['error', { maxNumericValue: 1500000 }], // 1.5MB
'dom-size': ['warn', { maxNumericValue: 800 }],
// JavaScript
'bootup-time': ['warn', { maxNumericValue: 3500 }],
'mainthread-work-breakdown': ['warn', { maxNumericValue: 4000 }],
}Best Practices
最佳实践
DO:
- Run Lighthouse CI on every PR
- Set realistic performance budgets
- Test both mobile and desktop
- Monitor trends over time
- Use temporary storage for PRs
- Setup LHCI server for history
- Test multiple pages
- Include authentication flows
DON'T:
- Set budgets too strict initially
- Only test homepage
- Ignore accessibility scores
- Skip mobile testing
- Test only in production
- Forget to warm up the server
- Ignore flaky metrics
建议:
- 每个PR都运行Lighthouse CI
- 设置合理的性能预算
- 同时测试移动端和桌面端
- 长期监控性能趋势
- 为PR使用临时存储
- 搭建LHCI服务器保存历史数据
- 测试多个页面
- 包含认证流程测试
禁忌:
- 初始设置过于严格的预算
- 仅测试首页
- 忽略无障碍访问评分
- 跳过移动端测试
- 仅在生产环境测试
- 忘记预热服务器
- 忽略不稳定的指标
Checklist
检查清单
- Lighthouse CI installed
- Configuration file created
- Performance budgets set
- CI/CD integration added
- Multiple URLs configured
- Mobile + Desktop testing
- Assertions configured
- PR comments enabled
- Monitoring setup
- Team notified of new checks
- 已安装Lighthouse CI
- 已创建配置文件
- 已设置性能预算
- 已集成CI/CD
- 已配置多个测试URL
- 已开启移动端+桌面端测试
- 已配置断言规则
- 已启用PR评论
- 已设置监控
- 已通知团队新增检查项