Оптимизация изображений в вебе: больше, чем просто сжатие

Болят метрики Core Web Vitals? Конверсии падают из-за долгой загрузки? Скорее всего, виноваты неоптимизированные изображения. Они составляют в среднем 42–60% веса страницы. Решение глубже, чем запуск imageOptim перед деплоем. Разберём инженерный пайплайн обработки изображений — от бэкенда до клиента.


Выбор формата: не просто JPEG vs PNG

Форматы — это компромисс между качеством, размером и поддержкой. Устаревшие подходы тонут в условных конструкциях для форматов. Современный стек:

html
<!-- HTML-решение через <picture> -->
<picture>
  <source srcset="photo.avif" type="image/avif"> <!-- 30% сжатие vs WebP -->
  <source srcset="photo.webp" type="image/webp"> 
  <img src="photo.jpg" alt="Описание"> <!-- Fallback -->
</picture>

Почему так?

  • AVIF даёт феноменальное сжатие (особенно для градиентов и теней), но поддерживается не везде (Safari добавил его только в 2023)
  • WebP — универсальный баланс (на 30% меньше JPEG при том же качестве). Поддержка >97%
  • JPEG/PNG — fallback для крайних случаев

Ошибка: Слепая конвертация всего в AVIF. Проверяйте поддержку User-Agent на бэкенде или используйте <picture>.


Инструменты: от FFmpeg до CDN Middleware

1. Batch-обработка (исходники)
Используйте скрипты на Node.js/Python вместо ручных GUI-инструментов. Пример конвеера на Sharp (Node.js):

javascript
const sharp = require('sharp');

async function optimizeImage(inputPath, outputPath) {
  await sharp(inputPath)
    .resize(1600) // Оптимизация под максимальный viewport
    .webp({ 
      quality: 80, 
      alphaQuality: 90, // Для прозрачности
      effort: 6 // Максимальное сжатие (медленнее)
    })
    .toFile(outputPath);
}

Что важно:

  • Ресайз до реально используемых размеров (1600px для десктопа хватит с головой)
  • Настройка effort для WebP: уровень 6 даёт лучшее сжатие, но требует в 2x больше CPU

2. CDN / Edge Middleware
Проблема: Хранить 5 версий (avif, webp, jpg @1x/@2x) каждого изображения нереально. Решение — обработка «на лету» через CDN:

  • Cloudflare Images: https://example.com/cdn-cgi/image/format=webp,width=800/img.jpg
  • AWS Lambda@Edge: Генерация преобразований при первом запросе + кеширование
  • Next.js Image Component: Авто-оптимизация через next/image
jsx
// Next.js пример
<Image
  src="/boat.jpg"
  alt="Лодка"
  width={1200}
  height={800}
  sizes="(max-width: 768px) 100vw, 50vw"
  quality={85}
  priority={true} // Для LCP-изображений
/>

Срыв покровов: next/image под капотом генерирует несколько версий, добавляет srcset и lazy loading.


Lazy Loading: не только атрибут loading="lazy"

Наивная реализация:

html
<img src="image.jpg" loading="lazy" alt="..."> 

Проблемы:

  • Не работает для iframe, кастомных галерей
  • Нет контроля над условиями загрузки
  • Не отменяет layout shift без width/height

Решение — Intersection Observer API:

javascript
const images = document.querySelectorAll('img[data-src]');

const observer = new IntersectionObserver((entries) => {
  entries.forEach(entry => {
    if (entry.isIntersecting) {
      const img = entry.target;
      img.src = img.dataset.src;
      observer.unobserve(img);
    }
  });
}, { 
  rootMargin: '200px' // Начинать загрузку за 200px до вьюпорта 
});

images.forEach(img => observer.observe(img));

Совет: Всегда указывать width и height в разметке (или через CSS aspect-ratio) для предотвращения CLS (Cumulative Layout Shift).


Анимации и GPU: когда PNG губит FPS

Даже оптимизированные статичные изображения могут вызывать лаги, если часто меняются (слайдеры, интерактивные галереи).

Техники:

  1. CSS will-change: transform — подсказывает браузеру о грядущих трансформах
  2. Преобразование в <canvas> для сложной анимации:
javascript
ctx.drawImage(img, 0, 0);
// Анимируем через requestAnimationFrame 
  1. Аппартное ускорение с translate3d() вместо top/left

Осторожно: Чрезмерное использование will-change провоцирует перерасход памяти.


Кеширование: ваш лучший друг

Безграмотные HTTP-заголовки сводят на нет оптимизацию. Так делает 74% сайтов (WPO Stats):

Идеальные хидеры для изображений:

text
Cache-Control: public, max-age=31536000, immutable
Content-Type: image/webp
  • max-age=31536000 (1 год) для статики
  • immutable — браузер не будет проверять изменения (только для версионированных URL)
  • CDN обязаны уважать Vary: Accept для разных форматов

Забытый нюанс: При использовании srcset с разной плотностью пикселей добавляйте дескриптор x:

html
<img 
  srcset="img-1x.webp 1x, img-2x.webp 2x"
  src="img-1x.webp" 
  alt="..."
>

Заключение: три принципа

  1. Автоматизируйте пайплайн — редакторы не должны знать про AVIF. Интегрируйте оптимизацию в CI/CD или используйте CDN.
  2. Тестируйте на реальных метриках — Lighthouse и WebPageTest покажут CLS, LCP, TBT.
  3. AVIF + WebP + conditional delivery = must have для современных приложений.

Оптимизацией изображений часто пренебрегают под давлением бизнес-задач. Но именно это становится скрытым камнем преткновения для UX и конверсий. Инструментарий 2023 года позволяет сделать этот процесс не болью, а незаметным инженерным стандартом.