Perf audit
Установка
npm i -D lighthouse playwright chrome-launcher
Скрипт
templates/perf.mjs:
import lighthouse from 'lighthouse';
import * as chromeLauncher from 'chrome-launcher';
import http from 'node:http';
import fs from 'node:fs/promises';
import path from 'node:path';
const file = process.argv[2];
if (!file) { console.error('Usage: node perf.mjs <file>'); process.exit(1); }
// Поднимем простейший http-сервер для замера (file:// не работает корректно)
const server = http.createServer(async (req, res) => {
const f = req.url === '/' ? file : '.' + req.url;
try {
const buf = await fs.readFile(f);
res.writeHead(200); res.end(buf);
} catch { res.writeHead(404); res.end(); }
});
await new Promise(r => server.listen(0, r));
const port = server.address().port;
const chrome = await chromeLauncher.launch({ chromeFlags: ['--headless'] });
const result = await lighthouse(`http://localhost:${port}/`, {
port: chrome.port, output: 'json',
onlyCategories: ['performance', 'accessibility', 'best-practices', 'seo'],
});
await chrome.kill();
server.close();
await fs.writeFile('perf-report.json', JSON.stringify(result.lhr, null, 2));
const cats = result.lhr.categories;
console.log('\nLighthouse:');
for (const [k, v] of Object.entries(cats)) {
const score = Math.round(v.score * 100);
const tag = score >= 90 ? '✓' : score >= 50 ? '~' : '✗';
console.log(` ${tag} ${k.padEnd(16)} ${score}`);
}
const audits = result.lhr.audits;
console.log('\nВеб-витал:');
for (const k of ['largest-contentful-paint', 'cumulative-layout-shift', 'total-blocking-time']) {
const a = audits[k];
console.log(` ${a.title.padEnd(28)} ${a.displayValue || '—'}`);
}
const fails = Object.values(audits)
.filter(a => a.score !== null && a.score < 0.9 && a.details)
.sort((x, y) => x.score - y.score)
.slice(0, 10);
console.log('\nТоп-10 замечаний:');
for (const a of fails) console.log(` - ${a.title} (${a.displayValue || ''})`);
process.exit(cats.performance.score >= 0.8 ? 0 : 1);
Использование
node perf.mjs index.html
# → perf-report.json + табличка в консоль
Ключевые метрики
- LCP < 2.5s — Largest Contentful Paint. Когда главный элемент стал видим.
- CLS < 0.1 — Cumulative Layout Shift. Сколько прыгает layout при загрузке.
- TBT < 300ms — Total Blocking Time. Сколько времени main thread заблокирован.
Типовые проблемы и решения
| Проблема |
Решение |
| Большой LCP |
Прелоад hero-картинки, fetchpriority="high", AVIF/WebP вместо PNG |
| Высокий CLS |
Указывай width+height на картинках, резервируй место под динамический контент |
| Много TBT |
Дробить JS, отложить аналитику, не блокировать main thread |
| Большой бандл |
Tree-shake, lazy-load роуты, не тащи moment.js |
| Шрифты |
font-display: swap, preload only critical weights |
| Картинки |
Современные форматы, loading="lazy" для below-the-fold |
Бюджеты
Для лендинга:
- HTML < 50KB
- CSS < 50KB
- JS < 200KB (gzipped)
- Картинки < 500KB суммарно на первый экран
- Шрифты < 100KB (max 2 веса)
Чего perf-audit не покажет
- Воспринимаемая скорость (animation jank).
- Реальные сетевые условия пользователей.
- Стоимость гидратации в SPA.
Для них — Chrome DevTools → Performance с CPU throttling 4x и Slow 3G.
Legacy reference
Прежняя расширенная версия скилла (дерево @2026-04-30) сохранена целиком в references/legacy-perf-audit.md. Секции там: Установка, CLI quick-check, Programmatic через Playwright + Lighthouse, Core Web Vitals — пороги, Типовые проблемы prototype'ов, Output — actionable отчёт, Core Web Vitals, Quick wins (savings ~500ms), Не критично, Когда НЕ делать perf-audit, Антипаттерны.
1---2name: perf-audit3description: Lighthouse в headless перед публикацией страницы: LCP, CLS, TBT, размер бандла, конкретные советы. Триггеры: «Core Web Vitals», «оптимизация перформанс».4---56# Perf audit78## Установка910```bash11npm i -D lighthouse playwright chrome-launcher12```1314## Скрипт1516`templates/perf.mjs`:1718```js19import lighthouse from 'lighthouse';20import * as chromeLauncher from 'chrome-launcher';21import http from 'node:http';22import fs from 'node:fs/promises';23import path from 'node:path';2425const file = process.argv[2];26if (!file) { console.error('Usage: node perf.mjs <file>'); process.exit(1); }2728// Поднимем простейший http-сервер для замера (file:// не работает корректно)29const server = http.createServer(async (req, res) => {30 const f = req.url === '/' ? file : '.' + req.url;31 try {32 const buf = await fs.readFile(f);33 res.writeHead(200); res.end(buf);34 } catch { res.writeHead(404); res.end(); }35});36await new Promise(r => server.listen(0, r));37const port = server.address().port;3839const chrome = await chromeLauncher.launch({ chromeFlags: ['--headless'] });40const result = await lighthouse(`http://localhost:${port}/`, {41 port: chrome.port, output: 'json',42 onlyCategories: ['performance', 'accessibility', 'best-practices', 'seo'],43});44await chrome.kill();45server.close();4647await fs.writeFile('perf-report.json', JSON.stringify(result.lhr, null, 2));4849const cats = result.lhr.categories;50console.log('\nLighthouse:');51for (const [k, v] of Object.entries(cats)) {52 const score = Math.round(v.score * 100);53 const tag = score >= 90 ? '✓' : score >= 50 ? '~' : '✗';54 console.log(` ${tag} ${k.padEnd(16)} ${score}`);55}5657const audits = result.lhr.audits;58console.log('\nВеб-витал:');59for (const k of ['largest-contentful-paint', 'cumulative-layout-shift', 'total-blocking-time']) {60 const a = audits[k];61 console.log(` ${a.title.padEnd(28)} ${a.displayValue || '—'}`);62}6364const fails = Object.values(audits)65 .filter(a => a.score !== null && a.score < 0.9 && a.details)66 .sort((x, y) => x.score - y.score)67 .slice(0, 10);68console.log('\nТоп-10 замечаний:');69for (const a of fails) console.log(` - ${a.title} (${a.displayValue || ''})`);7071process.exit(cats.performance.score >= 0.8 ? 0 : 1);72```7374## Использование7576```bash77node perf.mjs index.html78# → perf-report.json + табличка в консоль79```8081## Ключевые метрики8283- **LCP < 2.5s** — Largest Contentful Paint. Когда главный элемент стал видим.84- **CLS < 0.1** — Cumulative Layout Shift. Сколько прыгает layout при загрузке.85- **TBT < 300ms** — Total Blocking Time. Сколько времени main thread заблокирован.8687## Типовые проблемы и решения8889| Проблема | Решение |90|---|---|91| Большой LCP | Прелоад hero-картинки, `fetchpriority="high"`, AVIF/WebP вместо PNG |92| Высокий CLS | Указывай `width`+`height` на картинках, резервируй место под динамический контент |93| Много TBT | Дробить JS, отложить аналитику, не блокировать main thread |94| Большой бандл | Tree-shake, lazy-load роуты, не тащи moment.js |95| Шрифты | `font-display: swap`, preload only critical weights |96| Картинки | Современные форматы, `loading="lazy"` для below-the-fold |9798## Бюджеты99100Для лендинга:101- HTML < 50KB102- CSS < 50KB103- JS < 200KB (gzipped)104- Картинки < 500KB суммарно на первый экран105- Шрифты < 100KB (max 2 веса)106107## Чего perf-audit **не** покажет108109- Воспринимаемая скорость (animation jank).110- Реальные сетевые условия пользователей.111- Стоимость гидратации в SPA.112113Для них — Chrome DevTools → Performance с CPU throttling 4x и Slow 3G.114115## Legacy reference116117Прежняя расширенная версия скилла (дерево @2026-04-30) сохранена целиком в `references/legacy-perf-audit.md`. Секции там: Установка, CLI quick-check, Programmatic через Playwright + Lighthouse, Core Web Vitals — пороги, Типовые проблемы prototype'ов, Output — actionable отчёт, Core Web Vitals, Quick wins (savings ~500ms), Не критично, Когда НЕ делать perf-audit, Антипаттерны.