Настройка performance budget для сайта
Performance budget — набор ограничений на метрики производительности: размер бандла, время загрузки, Core Web Vitals. CI/CD падает при превышении бюджета, предотвращая деградацию сайта от версии к версии.
Ключевые метрики и ориентиры
| Метрика | Хорошо | Требует работы | Плохо |
|---|---|---|---|
| LCP (Largest Contentful Paint) | < 2.5s | 2.5–4s | > 4s |
| INP (Interaction to Next Paint) | < 200ms | 200–500ms | > 500ms |
| CLS (Cumulative Layout Shift) | < 0.1 | 0.1–0.25 | > 0.25 |
| TTFB (Time to First Byte) | < 600ms | 600ms–1.8s | > 1.8s |
| JS Bundle | < 200KB gzip | 200–500KB | > 500KB |
| Total Page Weight | < 1MB | 1–3MB | > 3MB |
Lighthouse CI: бюджеты по метрикам
// .lighthouserc.json
{
"ci": {
"collect": {
"url": [
"http://localhost:3000",
"http://localhost:3000/catalog",
"http://localhost:3000/checkout"
],
"numberOfRuns": 3,
"settings": {
"preset": "desktop",
"throttlingMethod": "simulate"
}
},
"assert": {
"assertions": {
"categories:performance": ["error", { "minScore": 0.85 }],
"categories:accessibility": ["error", { "minScore": 0.90 }],
"categories:seo": ["warn", { "minScore": 0.90 }],
"largest-contentful-paint": ["error", { "maxNumericValue": 2500 }],
"cumulative-layout-shift": ["error", { "maxNumericValue": 0.1 }],
"total-blocking-time": ["warn", { "maxNumericValue": 300 }],
"uses-optimized-images": ["warn"],
"unused-javascript": ["warn", { "maxNumericValue": 20000 }],
"render-blocking-resources": ["warn"]
}
},
"upload": {
"target": "temporary-public-storage"
}
}
}
Bundlesize: бюджет на размер JS/CSS
// bundlesize.config.json
{
"files": [
{
"path": ".next/static/chunks/main-*.js",
"maxSize": "60 kB"
},
{
"path": ".next/static/chunks/pages/index-*.js",
"maxSize": "50 kB"
},
{
"path": ".next/static/chunks/pages/catalog-*.js",
"maxSize": "80 kB"
},
{
"path": ".next/static/css/*.css",
"maxSize": "30 kB"
}
]
}
npm install --save-dev bundlesize
# В package.json:
# "scripts": { "bundlesize": "bundlesize" }
Webpack Bundle Analyzer
// next.config.js
const withBundleAnalyzer = require('@next/bundle-analyzer')({
enabled: process.env.ANALYZE === 'true',
});
module.exports = withBundleAnalyzer({
// конфиг Next.js
});
ANALYZE=true npm run build
# Открывает интерактивный отчёт в браузере
GitHub Actions: проверка бюджета
# .github/workflows/performance-budget.yml
name: Performance Budget
on: [pull_request]
jobs:
lighthouse-ci:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: '20' }
- run: npm ci && npm run build
- name: Start server
run: npm start &
- name: Wait for server
run: npx wait-on http://localhost:3000
- name: Run Lighthouse CI
run: npx lhci autorun
env:
LHCI_GITHUB_APP_TOKEN: ${{ secrets.LHCI_GITHUB_APP_TOKEN }}
bundlesize:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: '20' }
- run: npm ci && npm run build
- name: Check bundle size
run: npm run bundlesize
env:
CI_REPO_OWNER: ${{ github.repository_owner }}
CI_REPO_NAME: ${{ github.event.repository.name }}
CI_PULL_REQUEST: ${{ github.event.pull_request.number }}
CI_COMMIT_SHA: ${{ github.sha }}
BUNDLESIZE_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
Мониторинг в продакшене: SpeedCurve / DebugBear
Для непрерывного мониторинга Core Web Vitals в реальном трафике используют RUM (Real User Monitoring):
// Сбор Web Vitals из реального браузера
import { onLCP, onINP, onCLS, onFCP, onTTFB } from 'web-vitals';
function sendToAnalytics(metric: any) {
fetch('/api/vitals', {
method: 'POST',
body: JSON.stringify({
name: metric.name,
value: metric.value,
id: metric.id,
page: window.location.pathname,
}),
headers: { 'Content-Type': 'application/json' },
});
}
onLCP(sendToAnalytics);
onINP(sendToAnalytics);
onCLS(sendToAnalytics);
-- P75 Core Web Vitals за последние 24 часа
SELECT
name,
PERCENTILE_CONT(0.75) WITHIN GROUP (ORDER BY value) AS p75,
COUNT(*) AS samples
FROM web_vitals
WHERE created_at >= now() - interval '24 hours'
GROUP BY name;
Сроки
Настройка performance budget в CI с Lighthouse CI и bundlesize, RUM-мониторинг: 2–3 рабочих дня.







