Publikováno
· Autor: Zdeněk Hejzlar · Pod kapotou · 3 min čtení
Obsah článku
Obrázky jsou skoro vždy největší položka na stránce. Nepřevedené fotky z mobilu dokážou nafouknout galerii na desítky megabajtů a shodit Core Web Vitals. Ukážu, jak to řeším v Laravelu — automatický převod do WebP, konverze HEIC z iPhonu a responsivní <picture> s cachovanými rozměry.
Převod do WebP na pozadí
WebP je při stejné kvalitě zhruba o třetinu menší než JPEG. Nechci ale konverzi řešit ručně, tak si držím službu, která WebP variantu vygeneruje jednou a pak už jen vrací její URL.
use Illuminate\Support\Facades\Storage;
use Intervention\Image\ImageManager;
public function webpUrlFor(string $relative): ?string
{
$target = preg_replace('/\.\w+$/', '.webp', $relative);
if (! Storage::disk('public')->exists($target)) {
$source = Storage::disk('public')->path($relative);
$image = $this->manager->read($source);
Storage::disk('public')->put($target, (string) $image->toWebp(quality: 82));
}
return Storage::url($target);
}
Kvalita 82 je rozumný kompromis — na fotkách okem nepoznáte rozdíl, ale soubor spadne výrazně dolů.
HEIC a HEVC z iPhonu
Fotky z iPhonu chodí jako HEIC, videa jako HEVC — formáty, které polovina prohlížečů nezobrazí. Při nahrání je proto převádím na univerzální JPEG (respektive H.264), aby je přehrál kdokoli. Detekci řeším podle přípony a MIME typu, samotný převod přes imagick, u videa přes ffmpeg ve frontě.
if (in_array($file->getClientOriginalExtension(), ['heic', 'heif'])) {
$jpeg = $this->manager->read($file->getRealPath())->toJpeg(quality: 85);
Storage::disk('public')->put($path.'.jpg', (string) $jpeg);
}
Responsivní picture s cachovanými rozměry
Aby se stránka neposkakovala při načítání (CLS), potřebuje <img> znát rozměry předem. Čtení rozměrů z disku u každého requestu by ale bylo drahé, tak si je cachuju.
$meta = Cache::remember('img:meta:'.md5($relative), now()->addHours(6), fn () => [
'webp' => $optimizer->webpUrlFor($relative),
'dim' => $optimizer->dimensionsFor($relative),
]);
Výstup pak zabalím do jedné Blade komponenty, kterou používám všude — od výpisu projektů po hero obrázek článku:
<x-responsive-image
:path="$news->image"
:alt="$news->title"
loading="lazy"
sizes="(min-width: 992px) 720px, 100vw" />
Komponenta vyplní width, height, WebP <source> i aspect-ratio ve stylu. Prohlížeč si tak rezervuje místo dřív, než obrázek dorazí.
Shrnutí technických rozhodnutí
Generuj jednou, servíruj vždy. WebP i náhledy vznikají líně při prvním požadavku a pak se jen čtou.
Rozměry cachuj. Ušetří to čtení souboru u každého renderu a drží CLS na nule.
Lazy loading + fetchpriority. Hero obrázek
eagerahigh, zbyteklazy.
Závěr
Optimalizace obrázků není jednorázová akce, ale vrstva, která běží automaticky při každém uploadu. Nejvíc se vyplatí u galerií — přesně to jsem řešil u svatebního fotokoutku mezi projekty. Svižný web pak lépe konvertuje a míň zatěžuje server — širší pohled na výkon rozebírám v článku o optimalizaci výkonu Laravel aplikace a data z galerie zpřístupníte přes REST API. Potřebujete zrychlit existující web? Napište mi.