13. Оптимизация изображений
Qwik предоставляет встроенные инструменты для оптимизации изображений через пакет @unpic/qwik или интеграцию с Qwik City. Правильная работа с изображениями критически важна для производительности.
Базовая оптимизация
Заголовок раздела «Базовая оптимизация»Нативный <img> в HTML уже можно оптимизировать с помощью атрибутов:
// Базовая оптимизация без библиотек<img src="/images/hero.jpg" alt="Hero image" width={800} height={400} loading="lazy" // Ленивая загрузка decoding="async" // Асинхронное декодирование fetchpriority="high" // Для LCP изображений/>Image компонент с @unpic/qwik
Заголовок раздела «Image компонент с @unpic/qwik»import { Image } from '@unpic/qwik';
export const HeroImage = component$(() => { return ( <Image src="https://example.com/hero.jpg" alt="Hero" width={800} height={400} priority // Загрузить с высоким приоритетом layout="fullWidth" // Ширина во всю страницу /> );});imageTools плагин для Vite
Заголовок раздела «imageTools плагин для Vite»import { qwikVite } from '@builder.io/qwik/optimizer';import { qwikCity } from '@builder.io/qwik-city/vite';
export default defineConfig(() => ({ plugins: [ qwikCity(), qwikVite(), // Автоматическая оптимизация в dev-режиме ],}));// Импорт изображений с метаданнымиimport heroImage from './hero.jpg?w=800&h=400&format=webp';
export const Hero = component$(() => ( <img src={heroImage.src} width={heroImage.width} height={heroImage.height} />));Lazy Loading стратегии
Заголовок раздела «Lazy Loading стратегии»// 1. loading="lazy" — браузерная ленивая загрузка<img src="..." loading="lazy" alt="..." />
// 2. Intersection Observer — более гибкий контрольexport const LazyImage = component$<{ src: string; alt: string }>((props) => { const imgRef = useSignal<HTMLImageElement>(); const loaded = useSignal(false);
useVisibleTask$(() => { const observer = new IntersectionObserver( ([entry]) => { if (entry.isIntersecting) { loaded.value = true; observer.disconnect(); } }, { threshold: 0.1 } );
if (imgRef.value) observer.observe(imgRef.value); return () => observer.disconnect(); });
return ( <div ref={imgRef} style={{ minHeight: 200 }}> {loaded.value && ( <img src={props.src} alt={props.alt} /> )} </div> );});Адаптивные изображения
Заголовок раздела «Адаптивные изображения»export const ResponsiveImage = component$<{ src: string; alt: string }>((props) => { return ( <picture> {/* WebP для современных браузеров */} <source type="image/webp" srcset={\` \${props.src}?w=400&format=webp 400w, \${props.src}?w=800&format=webp 800w, \${props.src}?w=1200&format=webp 1200w \`} /> {/* Fallback JPEG */} <img src={props.src} alt={props.alt} sizes="(max-width: 400px) 100vw, (max-width: 800px) 50vw, 400px" loading="lazy" /> </picture> );});Placeholder и blur-up техника
Заголовок раздела «Placeholder и blur-up техника»export const BlurImage = component$<{ src: string; placeholder: string; // Base64 или низкоразрешённое изображение alt: string;}>((props) => { const isLoaded = useSignal(false);
return ( <div style="position: relative; overflow: hidden;"> {/* Placeholder (blur) */} <img src={props.placeholder} alt="" aria-hidden="true" style={{ position: 'absolute', inset: 0, filter: 'blur(20px)', transform: 'scale(1.1)', opacity: isLoaded.value ? 0 : 1, transition: 'opacity 0.3s', }} /> {/* Основное изображение */} <img src={props.src} alt={props.alt} loading="lazy" onLoad$={() => { isLoaded.value = true; }} style={{ opacity: isLoaded.value ? 1 : 0, transition: 'opacity 0.3s' }} /> </div> );});