Что такое Lighthouse?

Lighthouse — это автоматизированный инструмент с открытым исходным кодом от Google для улучшения качества веб-страниц. Он предоставляет комплексные аудиты производительности, доступности, progressive web apps, SEO и лучших практик. Для QA-инженеров Lighthouse необходим для валидации соответствия приложений современным стандартам веб-производительности, особенно Core Web Vitals — ключевым метрикам Google для пользовательского опыта.

Почему Lighthouse Важен для QA

  • Core Web Vitals Влияют на SEO - Google использует LCP, FID и CLS как факторы ранжирования
  • Валидация Пользовательского Опыта - Квантифицируйте воспринимаемую производительность реальными метриками
  • Интеграция CI/CD - Автоматизируйте регрессионное тестирование производительности в пайплайнах
  • Практичные Инсайты - Получайте конкретные рекомендации по улучшению производительности
  • Мобильная Производительность - Тестируйте адаптивный дизайн и мобильные проблемы

Core Web Vitals Подробно

Core Web Vitals — это три ключевые метрики, измеряющие пользовательский опыт в реальном мире:

1. Largest Contentful Paint (LCP)

Что измеряет: Производительность загрузки - когда самый большой элемент контента становится видимым.

Пороги:

  • Хорошо: ≤ 2.5 секунды
  • Требует Улучшения: 2.5 - 4.0 секунды
  • Плохо: > 4.0 секунд

Частые Проблемы:

// Проблема: Блокирующий рендеринг JavaScript
<script src="analytics.js"></script>

// Решение: Отложить некритичные скрипты
<script src="analytics.js" defer></script>

// Проблема: Неоптимизированные изображения
<img src="hero-10mb.jpg" alt="Hero">

// Решение: Современные форматы + адаптивные изображения
<picture>
  <source srcset="hero.avif" type="image/avif">
  <source srcset="hero.webp" type="image/webp">
  <img src="hero.jpg" alt="Hero" loading="lazy">
</picture>

Стратегии Оптимизации:

  • Использовать CDN для статических ресурсов
  • Реализовать lazy loading для изображений below-fold
  • Предзагружать критические ресурсы: <link rel="preload" href="hero.jpg" as="image">
  • Оптимизировать время ответа сервера (TTFB < 600ms)
  • Использовать resource hints: dns-prefetch, preconnect

2. First Input Delay (FID) / Interaction to Next Paint (INP)

Что измеряет: Интерактивность - задержка между взаимодействием пользователя и ответом браузера.

Пороги FID (заменяется на INP):

  • Хорошо: ≤ 100ms
  • Требует Улучшения: 100 - 300ms
  • Плохо: > 300ms

Пороги INP (новая метрика с 2024):

  • Хорошо: ≤ 200ms
  • Требует Улучшения: 200 - 500ms
  • Плохо: > 500ms

Частые Проблемы:

// Проблема: Длинные JavaScript задачи блокируют главный поток
function processHugeDataset() {
  // Синхронный цикл обрабатывает 100k элементов
  for (let i = 0; i < 100000; i++) {
    heavyCalculation(data[i]);
  }
}

// Решение: Разбить на маленькие chunks с setTimeout
async function processHugeDataset() {
  const chunkSize = 1000;
  for (let i = 0; i < data.length; i += chunkSize) {
    const chunk = data.slice(i, i + chunkSize);
    chunk.forEach(item => heavyCalculation(item));

    // Уступить браузеру каждый chunk
    await new Promise(resolve => setTimeout(resolve, 0));
  }
}

// Решение: Использовать Web Workers для тяжелых вычислений
// main.js
const worker = new Worker('calculator.worker.js');
worker.postMessage({ data: largeDataset });
worker.onmessage = (e) => {
  displayResults(e.data);
};

// calculator.worker.js
self.onmessage = (e) => {
  const results = heavyCalculation(e.data);
  self.postMessage(results);
};

Стратегии Оптимизации:

  • Разделять длинные задачи на маленькие chunks (< 50ms каждый)
  • Использовать code splitting для уменьшения размера JavaScript bundle
  • Откладывать сторонние скрипты
  • Реализовать service workers для лучшего кэширования
  • Использовать requestIdleCallback для несрочной работы

3. Cumulative Layout Shift (CLS)

Что измеряет: Визуальная стабильность - неожиданные сдвиги layout во время загрузки страницы.

Пороги:

  • Хорошо: ≤ 0.1
  • Требует Улучшения: 0.1 - 0.25
  • Плохо: > 0.25

Частые Проблемы:

<!-- Проблема: Изображения без размеров -->
<img src="product.jpg" alt="Product">

<!-- Решение: Всегда указывать width/height -->
<img src="product.jpg" alt="Product" width="800" height="600">

<!-- Проблема: Динамическая вставка контента -->
<div id="banner"></div>
<script>
  // Загружает рекламу асинхронно, толкая контент вниз
  loadAdvertisement('#banner');
</script>

<!-- Решение: Резервировать место для динамического контента -->
<div id="banner" style="min-height: 250px;">
  <!-- Реклама загружается здесь без layout shift -->
</div>
/* Проблема: Веб-шрифты вызывают layout shift (FOIT/FOUT) */
@font-face {
  font-family: 'CustomFont';
  src: url('custom-font.woff2') format('woff2');
}

/* Решение: Использовать font-display для контроля поведения загрузки */
@font-face {
  font-family: 'CustomFont';
  src: url('custom-font.woff2') format('woff2');
  font-display: swap; /* Показать fallback сразу, заменить когда загрузится */
}

/* Лучше: Предзагрузить критические шрифты */
/* В HTML: <link rel="preload" href="custom-font.woff2" as="font" crossorigin> */

Стратегии Оптимизации:

  • Всегда включать атрибуты размера на изображениях и видео
  • Резервировать место для рекламы и embed’ов с min-height
  • Избегать вставки контента выше существующего контента
  • Использовать анимации transform вместо свойств, триггерящих layout
  • Предзагружать кастомные шрифты и использовать font-display: swap

Запуск Lighthouse Тестов

1. Chrome DevTools (Ручное Тестирование)

# Открыть Chrome DevTools
# 1. Нажать F12 или Cmd+Option+I
# 2. Перейти на вкладку "Lighthouse"
# 3. Выбрать категории (Performance, Accessibility, etc.)
# 4. Выбрать устройство (Mobile/Desktop)
# 5. Нажать "Analyze page load"

Лучшие Практики для Ручных Тестов:

  • Тестировать в режиме Инкогнито (без вмешательства расширений)
  • Использовать throttling для симуляции реальных условий (Slow 4G, 4x CPU slowdown)
  • Запускать несколько тестов и усреднять результаты (могут варьироваться ±5 пунктов)
  • Тестировать и мобильную, и десктопную конфигурации

2. Lighthouse CLI (Автоматизированное Тестирование)

# Установить Lighthouse
npm install -g lighthouse

# Базовый тест
lighthouse https://example.com

# Мобильный тест с форматами вывода
lighthouse https://example.com \
  --output html,json,csv \
  --output-path ./reports/report \
  --view \
  --chrome-flags="--headless"

# Тест с конкретным throttling
lighthouse https://example.com \
  --throttling.rttMs=150 \
  --throttling.throughputKbps=1600 \
  --throttling.cpuSlowdownMultiplier=4

# Тестировать только конкретные категории
lighthouse https://example.com \
  --only-categories=performance,accessibility

# Кастомный файл конфигурации
lighthouse https://example.com \
  --config-path=./lighthouse-config.js

3. Lighthouse CI (Непрерывная Интеграция)

# .github/workflows/lighthouse.yml
name: Lighthouse CI

on:
  pull_request:
    branches: [main]

jobs:
  lighthouse:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3

      - name: Setup Node.js
        uses: actions/setup-node@v3
        with:
          node-version: '18'

      - name: Install dependencies
        run: npm ci

      - name: Build application
        run: 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: |
          npm install -g @lhci/cli
          lhci autorun
        env:
          LHCI_GITHUB_APP_TOKEN: ${{ secrets.LHCI_GITHUB_APP_TOKEN }}

      - name: Upload results
        uses: actions/upload-artifact@v3
        with:
          name: lighthouse-reports
          path: .lighthouseci/

Конфигурация lighthouserc.json:

{
  "ci": {
    "collect": {
      "url": ["http://localhost:3000"],
      "numberOfRuns": 3,
      "settings": {
        "preset": "desktop",
        "throttling": {
          "rttMs": 40,
          "throughputKbps": 10240,
          "cpuSlowdownMultiplier": 1
        }
      }
    },
    "assert": {
      "preset": "lighthouse:recommended",
      "assertions": {
        "categories:performance": ["error", {"minScore": 0.9}],
        "categories:accessibility": ["error", {"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"
    }
  }
}

Интерпретация Lighthouse Оценок

Разбивка Оценки Производительности

Оценка Performance (0-100) взвешивается по нескольким метрикам:

МетрикаВесОписание
First Contentful Paint (FCP)10%Первый текст/изображение отрендерено
Largest Contentful Paint (LCP)25%Самый большой элемент контента виден
Total Blocking Time (TBT)30%Общее время блокировки главного потока
Cumulative Layout Shift (CLS)25%Оценка визуальной стабильности
Speed Index10%Как быстро визуально отображается контент

Диапазоны Оценок:

  • 90-100 (Зеленый): Хорошо - соответствует лучшим практикам производительности
  • 50-89 (Оранжевый): Требует улучшения - нужна некоторая оптимизация
  • 0-49 (Красный): Плохо - значительные проблемы производительности

Стратегия Бюджета Производительности

// lighthouserc-budget.json
{
  "ci": {
    "assert": {
      "assertions": {
        // Пороги Core Web Vitals
        "largest-contentful-paint": ["error", {"maxNumericValue": 2500}],
        "cumulative-layout-shift": ["error", {"maxNumericValue": 0.1}],
        "total-blocking-time": ["error", {"maxNumericValue": 300}],

        // Бюджеты ресурсов
        "resource-summary:script:size": ["error", {"maxNumericValue": 300000}],
        "resource-summary:image:size": ["error", {"maxNumericValue": 500000}],
        "resource-summary:stylesheet:size": ["error", {"maxNumericValue": 50000}],
        "resource-summary:document:size": ["error", {"maxNumericValue": 50000}],
        "resource-summary:font:size": ["error", {"maxNumericValue": 100000}],
        "resource-summary:third-party:size": ["error", {"maxNumericValue": 200000}],

        // Бюджеты количества запросов
        "resource-summary:script:count": ["error", {"maxNumericValue": 15}],
        "resource-summary:third-party:count": ["error", {"maxNumericValue": 10}]
      }
    }
  }
}

Продвинутые Техники Lighthouse

1. Кастомные Аудиты

// custom-audit.js
class CustomPerformanceAudit {
  static get meta() {
    return {
      id: 'custom-api-performance',
      title: 'Время Ответа API',
      failureTitle: 'Ответы API медленные',
      description: 'Измеряет времена ответа API эндпоинтов',
      requiredArtifacts: ['devtoolsLogs']
    };
  }

  static async audit(artifacts) {
    const devtools = artifacts.devtoolsLogs.defaultPass;
    const networkRecords = /* извлечь сетевые записи */;

    const apiCalls = networkRecords.filter(r =>
      r.url.includes('/api/') && r.finished
    );

    const avgResponseTime = apiCalls.reduce((sum, call) =>
      sum + call.endTime - call.startTime, 0
    ) / apiCalls.length;

    const passed = avgResponseTime < 500; // порог 500ms

    return {
      score: passed ? 1 : 0,
      numericValue: avgResponseTime,
      displayValue: `${Math.round(avgResponseTime)}ms среднее`,
      details: {
        type: 'table',
        headings: [
          {key: 'url', text: 'URL'},
          {key: 'responseTime', text: 'Время Ответа'}
        ],
        items: apiCalls.map(call => ({
          url: call.url,
          responseTime: `${Math.round(call.endTime - call.startTime)}ms`
        }))
      }
    };
  }
}

module.exports = CustomPerformanceAudit;

2. Тестирование User Flow (Новое в Lighthouse 9+)

// user-flow-test.js
import {startFlow} from 'lighthouse/lighthouse-core/fraggle-rock/api.js';
import puppeteer from 'puppeteer';

async function runUserFlow() {
  const browser = await puppeteer.launch({headless: 'new'});
  const page = await browser.newPage();

  const flow = await startFlow(page, {name: 'E-commerce checkout flow'});

  // Шаг 1: Навигация на homepage
  await flow.navigate('https://example.com', {
    stepName: 'Homepage'
  });

  // Шаг 2: Поиск продукта
  await flow.startTimespan({stepName: 'Взаимодействие поиска'});
  await page.type('#search', 'laptop');
  await page.click('#search-button');
  await page.waitForSelector('.search-results');
  await flow.endTimespan();

  // Шаг 3: Страница деталей продукта
  await flow.navigate('https://example.com/product/123', {
    stepName: 'Страница продукта'
  });

  // Шаг 4: Добавление в корзину взаимодействие
  await flow.startTimespan({stepName: 'Добавить в корзину'});
  await page.click('#add-to-cart');
  await page.waitForSelector('.cart-notification');
  await flow.endTimespan();

  // Шаг 5: Снимок checkout
  await flow.snapshot({stepName: 'Состояние страницы корзины'});

  // Генерация отчета
  const report = await flow.generateReport();
  fs.writeFileSync('user-flow-report.html', report);

  await browser.close();
}

runUserFlow();

3. Дашборд Мониторинга Производительности

# lighthouse-monitor.py
import subprocess
import json
import datetime
from influxdb import InfluxDBClient

def run_lighthouse(url):
    """Запустить Lighthouse и вернуть метрики"""
    result = subprocess.run([
        'lighthouse',
        url,
        '--output=json',
        '--output-path=stdout',
        '--chrome-flags="--headless"'
    ], capture_output=True, text=True)

    return json.loads(result.stdout)

def extract_metrics(report):
    """Извлечь ключевые метрики из отчета Lighthouse"""
    audits = report['audits']

    return {
        'performance_score': report['categories']['performance']['score'] * 100,
        'fcp': audits['first-contentful-paint']['numericValue'],
        'lcp': audits['largest-contentful-paint']['numericValue'],
        'tbt': audits['total-blocking-time']['numericValue'],
        'cls': audits['cumulative-layout-shift']['numericValue'],
        'speed_index': audits['speed-index']['numericValue'],
    }

def send_to_influxdb(metrics, url):
    """Отправить метрики в InfluxDB для визуализации Grafana"""
    client = InfluxDBClient(host='localhost', port=8086, database='lighthouse')

    point = {
        'measurement': 'performance',
        'tags': {
            'url': url,
            'device': 'mobile'
        },
        'time': datetime.datetime.utcnow().isoformat(),
        'fields': metrics
    }

    client.write_points([point])

# Мониторить страницы каждый час
urls = [
    'https://example.com',
    'https://example.com/products',
    'https://example.com/checkout'
]

for url in urls:
    report = run_lighthouse(url)
    metrics = extract_metrics(report)
    send_to_influxdb(metrics, url)
    print(f"✓ {url}: Оценка Производительности {metrics['performance_score']}")

Частые Узкие Места и Исправления

Проблема: Большие JavaScript Бандлы

# Анализ размера bundle
npm install -g webpack-bundle-analyzer

# Добавить в webpack.config.js
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;

module.exports = {
  plugins: [
    new BundleAnalyzerPlugin()
  ]
};

# Build и анализ
npm run build

Решения:

  • Code splitting: import('./heavy-component.js').then(module => ...)
  • Tree shaking: Убедиться "sideEffects": false в package.json
  • Удалить неиспользуемые зависимости
  • Использовать динамические импорты для route-based code splitting

Проблема: Блокирующие Рендеринг Ресурсы

<!-- Проблема: Блокирующий CSS -->
<link rel="stylesheet" href="styles.css">

<!-- Решение: Критический CSS inline, отложить некритичный -->
<style>
  /* Критический above-fold CSS inline */
  .header { /* ... */ }
  .hero { /* ... */ }
</style>
<link rel="preload" href="styles.css" as="style" onload="this.onload=null;this.rel='stylesheet'">
<noscript><link rel="stylesheet" href="styles.css"></noscript>

Проблема: Неоптимизированные Изображения

# Установить инструменты оптимизации изображений
npm install -D imagemin imagemin-webp imagemin-avif

# optimize-images.js
const imagemin = require('imagemin');
const imageminWebp = require('imagemin-webp');
const imageminAvif = require('imagemin-avif');

(async () => {
  await imagemin(['images/*.{jpg,png}'], {
    destination: 'images/optimized',
    plugins: [
      imageminWebp({quality: 80}),
      imageminAvif({quality: 65})
    ]
  });
})();

Заключение

Lighthouse — незаменимый инструмент для современного тестирования веб-производительности. Интегрируя Lighthouse в ваш QA workflow — от ручных аудитов DevTools до автоматизированных CI/CD (как обсуждается в Load Testing with JMeter: Complete Guide) пайплайнов — вы гарантируете, что приложения соответствуют стандартам Core Web Vitals и обеспечивают исключительный пользовательский опыт.

Ключевые Выводы:

  • Фокусируйтесь на Core Web Vitals: LCP, INP/FID, CLS
  • Автоматизируйте Lighthouse тесты в CI/CD (как обсуждается в Burp Suite for QA Engineers: Complete Security Testing Guide) пайплайнах
  • Устанавливайте бюджеты производительности и применяйте их
  • Используйте User Flow тестирование для сложных взаимодействий
  • Мониторьте производительность во времени с дашбордами
  • Оптимизируйте изображения, JavaScript и пути рендеринга

Производительность — это функция, а не запоздалая мысль. Сделайте Lighthouse-тестирование ключевой частью вашего QA процесса.