Болят метрики Core Web Vitals? Конверсии падают из-за долгой загрузки? Скорее всего, виноваты неоптимизированные изображения. Они составляют в среднем 42–60% веса страницы. Решение глубже, чем запуск imageOptim
перед деплоем. Разберём инженерный пайплайн обработки изображений — от бэкенда до клиента.
Выбор формата: не просто JPEG vs PNG
Форматы — это компромисс между качеством, размером и поддержкой. Устаревшие подходы тонут в условных конструкциях для форматов. Современный стек:
<!-- 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):
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
// 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"
Наивная реализация:
<img src="image.jpg" loading="lazy" alt="...">
Проблемы:
- Не работает для iframe, кастомных галерей
- Нет контроля над условиями загрузки
- Не отменяет layout shift без
width
/height
Решение — Intersection Observer API:
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
Даже оптимизированные статичные изображения могут вызывать лаги, если часто меняются (слайдеры, интерактивные галереи).
Техники:
- CSS
will-change: transform
— подсказывает браузеру о грядущих трансформах - Преобразование в
<canvas>
для сложной анимации:
ctx.drawImage(img, 0, 0);
// Анимируем через requestAnimationFrame
- Аппартное ускорение с
translate3d()
вместоtop/left
Осторожно: Чрезмерное использование will-change
провоцирует перерасход памяти.
Кеширование: ваш лучший друг
Безграмотные HTTP-заголовки сводят на нет оптимизацию. Так делает 74% сайтов (WPO Stats):
Идеальные хидеры для изображений:
Cache-Control: public, max-age=31536000, immutable
Content-Type: image/webp
max-age=31536000
(1 год) для статикиimmutable
— браузер не будет проверять изменения (только для версионированных URL)- CDN обязаны уважать
Vary: Accept
для разных форматов
Забытый нюанс: При использовании srcset
с разной плотностью пикселей добавляйте дескриптор x
:
<img
srcset="img-1x.webp 1x, img-2x.webp 2x"
src="img-1x.webp"
alt="..."
>
Заключение: три принципа
- Автоматизируйте пайплайн — редакторы не должны знать про AVIF. Интегрируйте оптимизацию в CI/CD или используйте CDN.
- Тестируйте на реальных метриках — Lighthouse и WebPageTest покажут CLS, LCP, TBT.
- AVIF + WebP + conditional delivery = must have для современных приложений.
Оптимизацией изображений часто пренебрегают под давлением бизнес-задач. Но именно это становится скрытым камнем преткновения для UX и конверсий. Инструментарий 2023 года позволяет сделать этот процесс не болью, а незаметным инженерным стандартом.