Почему оптимизация изображений важна
Изображения составляют 50-70% веса страницы. Правильная оптимизация критически влияет на скорость загрузки, Core Web Vitals и пользовательский опыт.
Современные форматы
| Формат | Сжатие | Поддержка | Применение |
|---|---|---|---|
| JPEG | Хорошее | 100% | Фото |
| PNG | Без потерь | 100% | Графика, прозрачность |
| WebP | Отличное | 97% | Универсальный |
| AVIF | Лучшее | 85% | Фото, где поддерживается |
<!-- Fallback для старых браузеров -->
<picture>
<source srcset="/image.avif" type="image/avif" />
<source srcset="/image.webp" type="image/webp" />
<img src="/image.jpg" alt="Description" />
</picture>
Next.js Image Component
Next.js автоматически оптимизирует изображения:
import Image from 'next/image';
// Базовое использование
<Image
src="/hero.jpg"
alt="Hero image"
width={1200}
height={600}
priority // Для LCP изображений
/>
// Responsive изображение
<Image
src="/product.jpg"
alt="Product"
fill
sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw"
className="object-cover"
/>
// С blur placeholder
import heroImage from '@/public/hero.jpg';
<Image
src={heroImage}
alt="Hero"
placeholder="blur" // Автоматический blur для статических импортов
/>
// Динамический blur placeholder
<Image
src={dynamicUrl}
alt="Dynamic"
placeholder="blur"
blurDataURL="data:image/jpeg;base64,/9j/4AAQSkZJRg..."
/>
Конфигурация Next.js
// next.config.js
module.exports = {
images: {
remotePatterns: [
{
protocol: 'https',
hostname: 'images.unsplash.com',
},
{
protocol: 'https',
hostname: 'cdn.example.com',
}
],
formats: ['image/avif', 'image/webp'],
deviceSizes: [640, 750, 828, 1080, 1200, 1920, 2048],
imageSizes: [16, 32, 48, 64, 96, 128, 256, 384],
minimumCacheTTL: 60 * 60 * 24 * 30, // 30 дней
}
};
Lazy Loading
// Native lazy loading
<img src="/image.jpg" alt="..." loading="lazy" />
// Next.js Image — lazy по умолчанию
<Image src="/image.jpg" alt="..." width={800} height={600} />
// Отключение lazy для важных изображений
<Image
src="/hero.jpg"
alt="Hero"
width={1200}
height={600}
priority // Загружается сразу
loading="eager"
/>
// Intersection Observer для кастомной логики
function LazyImage({ src, alt }) {
const [isVisible, setIsVisible] = useState(false);
const ref = useRef<HTMLDivElement>(null);
useEffect(() => {
const observer = new IntersectionObserver(
([entry]) => {
if (entry.isIntersecting) {
setIsVisible(true);
observer.disconnect();
}
},
{ rootMargin: '100px' }
);
if (ref.current) observer.observe(ref.current);
return () => observer.disconnect();
}, []);
return (
<div ref={ref}>
{isVisible ? (
<img src={src} alt={alt} />
) : (
<div className="skeleton" />
)}
</div>
);
}
Responsive Images
<!-- srcset для разных размеров экрана -->
<img
src="/image-800.jpg"
srcset="
/image-400.jpg 400w,
/image-800.jpg 800w,
/image-1200.jpg 1200w
"
sizes="(max-width: 600px) 100vw, 50vw"
alt="Responsive image"
/>
<!-- Art direction с picture -->
<picture>
<source
media="(max-width: 767px)"
srcset="/hero-mobile.webp"
/>
<source
media="(max-width: 1023px)"
srcset="/hero-tablet.webp"
/>
<img src="/hero-desktop.webp" alt="Hero" />
</picture>
CDN и кэширование
// Cloudinary URL API
function getOptimizedUrl(publicId: string, options: ImageOptions) {
const { width, height, quality = 'auto', format = 'auto' } = options;
return `https://res.cloudinary.com/demo/image/upload/` +
`w_${width},h_${height},c_fill,q_${quality},f_${format}/` +
publicId;
}
// Использование
<img src={getOptimizedUrl('products/shoe', { width: 400, height: 400 })} />
// Imgix
const imgixUrl = `/placeholders/image-placeholder.svg?w=800&auto=format,compress`;
Генерация blur placeholder
// Генерация base64 blur на сервере
import sharp from 'sharp';
async function generateBlurPlaceholder(imagePath: string): Promise<string> {
const buffer = await sharp(imagePath)
.resize(10, 10, { fit: 'inside' })
.blur()
.toBuffer();
return `data:image/jpeg;base64,${buffer.toString('base64')}`;
}
// Использование с Prisma
const product = await prisma.product.findUnique({
where: { id },
select: {
image: true,
blurDataURL: true
}
});
<Image
src={product.image}
alt="Product"
placeholder="blur"
blurDataURL={product.blurDataURL}
/>
Чек-лист оптимизации
- ✅ Используйте WebP/AVIF с fallback
- ✅ Указывайте width и height (избегаем CLS)
- ✅ Lazy loading для изображений ниже fold
- ✅ priority для LCP изображений
- ✅ Responsive sizes для разных экранов
- ✅ Blur placeholder для лучшего UX
- ✅ CDN для быстрой доставки
Заключение
Оптимизация изображений — один из самых эффективных способов улучшить производительность. Next.js Image делает большую часть работы автоматически. Используйте современные форматы, lazy loading и CDN для максимальной скорости.
Каждый сэкономленный килобайт — это быстрее загрузка и лучший UX. Оптимизируйте изображения в первую очередь.
