Оптимизация изображений: 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-компонент для adaptive image
// 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));
}
Оптимизация качества
Типичные рекомендации по quality:
| Тип изображения | WebP quality | AVIF quality |
|---|---|---|
| Фото товара | 82–85 | 65–70 |
| Hero-изображение | 85–88 | 70–75 |
| Иконки, логотипы | Используйте SVG | — |
| Скриншоты, UI | 90 | 75 |
Blur placeholder (LQIP)
// Генерация tiny 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
// Сохранить в БД, использовать как placeholder
<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 дня для настройки конвертации и медиабиблиотеки.







