Оптимізація зображень: WebP, AVIF, lazy loading
Зображення — 50–70% ваги середньої веб-сторінки. Конвертація в WebP дає 25–35% економії порівняно з JPEG; AVIF — ще 20% зверху. Lazy loading економить трафік та прискорює завантаження першого екрана.
Формати: JPEG → WebP → AVIF
| Формат | Підтримка браузерів | Економія vs JPEG |
|---|---|---|
| JPEG | 100% | — |
| WebP | 96% | 25–35% |
| AVIF | 88% | 40–60% |
| WebP (lossless) | 96% | 20–30% vs PNG |
Елемент <picture> для прогресивного fallback:
<picture>
<source type="image/avif"
srcset="/images/product-400.avif 400w,
/images/product-800.avif 800w,
/images/product-1200.avif 1200w"
sizes="(max-width: 640px) 100vw, (max-width: 1024px) 50vw, 400px">
<source type="image/webp"
srcset="/images/product-400.webp 400w,
/images/product-800.webp 800w,
/images/product-1200.webp 1200w"
sizes="(max-width: 640px) 100vw, (max-width: 1024px) 50vw, 400px">
<img src="/images/product-800.jpg"
width="800" height="800"
alt="Назва продукту"
loading="lazy"
decoding="async">
</picture>
Генерація конвертованих форматів у Laravel
// Використання spatie/laravel-medialibrary з конверсіями
class Product extends Model
{
use InteractsWithMedia;
public function registerMediaConversions(?Media $media = null): void
{
$this->addMediaConversion('thumb')
->width(400)->height(400)
->format('webp')
->quality(80)
->performOnCollections('images');
$this->addMediaConversion('medium')
->width(800)->height(800)
->format('webp')
->quality(82)
->performOnCollections('images');
$this->addMediaConversion('avif-medium')
->width(800)->height(800)
->format('avif')
->quality(65)
->performOnCollections('images');
}
}
// Blade-компонент для адаптивного зображення
// resources/views/components/image.blade.php
@props(['media', 'alt', 'sizes' => '100vw', 'loading' => 'lazy'])
<picture>
@if ($media->hasGeneratedConversion('avif-medium'))
<source type="image/avif"
srcset="{{ $media->getUrl('avif-medium') }}"
sizes="{{ $sizes }}">
@endif
<source type="image/webp"
srcset="{{ $media->getUrl('medium') }}"
sizes="{{ $sizes }}">
<img src="{{ $media->getUrl() }}"
width="{{ $media->getCustomProperty('width') }}"
height="{{ $media->getCustomProperty('height') }}"
alt="{{ $alt }}"
loading="{{ $loading }}"
decoding="async">
</picture>
Nginx: автоматична віддача WebP
Якщо немає можливості генерувати на льоту — конвертуйте заздалегідь та віддавайте через Nginx:
map $http_accept $webp_suffix {
default "";
"~*webp" ".webp";
}
location ~* \.(jpg|jpeg|png)$ {
add_header Vary Accept;
try_files $uri$webp_suffix $uri =404;
expires 30d;
}
Lazy loading
<!-- Нативний lazy loading -->
<img src="photo.webp" loading="lazy" alt="...">
<!-- LCP-зображення — НІКОЛИ не lazy -->
<img src="hero.webp" loading="eager" fetchpriority="high" alt="...">
Нативний lazy loading підтримується у всіх сучасних браузерах. Зображення завантажуються коли входять у viewport з невеликим запасом (звичайно 1200px до появи).
Intersection Observer для кастомного lazy loading
// Для background-image та нестандартних випадків
function lazyLoadBackgrounds() {
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const el = entry.target as HTMLElement;
el.style.backgroundImage = `url(${el.dataset.bg})`;
el.removeAttribute('data-bg');
observer.unobserve(el);
}
});
}, { rootMargin: '200px' });
document.querySelectorAll('[data-bg]').forEach(el => observer.observe(el));
}
Оптимізація якості
Типові рекомендації по якості:
| Тип зображення | WebP якість | AVIF якість |
|---|---|---|
| Фото товару | 82–85 | 65–70 |
| Hero-зображення | 85–88 | 70–75 |
| Іконки, логотипи | Використовуйте SVG | — |
| Скриншоти, UI | 90 | 75 |
Blur placeholder (LQIP)
// Генерація невеликого placeholder при завантаженні
use Intervention\Image\ImageManager;
$manager = ImageManager::gd();
$image = $manager->read($file->path());
$lqip = $image->scale(width: 20)
->toJpeg(quality: 30)
->toDataUri(); // base64 data URI
<img src="{{ $lqip }}"
data-src="/images/product-full.webp"
style="filter: blur(10px); transition: filter 0.3s"
onload="this.style.filter='none'; this.src=this.dataset.src"
alt="...">
Час оптимізації: 1–2 дні для конвертації та налаштування медіабіблліотеки.







