loading lazy
loading="lazy"-- нативный HTML-атрибут для ленивой загрузки изображений и iframe. Браузер откладывает загрузку ресурса до момента, когда он приближается к viewport.
Зачем нужно
Средняя веб-страница содержит десятки изображений. Без lazy loading браузер загружает все сразу при открытии страницы, даже те, что внизу за пределами экрана. Это замедляет начальную загрузку, тратит трафик и ухудшает Core Web Vitals (LCP, FCP). loading="lazy" решает это одним атрибутом.
Где используется
- Длинные страницы с множеством изображений (блоги, каталоги)
- Карточки товаров в интернет-магазинах
- Встраивание iframe (YouTube, карты)
- Бесконечный скролл (infinite scroll)
Предпосылки
Базовый синтаксис
<!-- Ленивая загрузка изображения -->
<img
src="product.jpg"
alt="Товар"
width="400"
height="300"
loading="lazy"
>
<!-- Ленивая загрузка iframe -->
<iframe
src="https://www.youtube.com/embed/dQw4w9WgXcQ"
width="560"
height="315"
loading="lazy"
title="Видео"
></iframe>
Значения атрибута loading
<!-- lazy: загрузить когда приблизится к viewport -->
<img src="photo.jpg" loading="lazy" alt="Фото">
<!-- eager: загрузить немедленно (поведение по умолчанию) -->
<img src="hero.jpg" loading="eager" alt="Герой">
<!-- По умолчанию (если не указано) — eager -->
<img src="photo.jpg" alt="Фото">
fetchpriority -- приоритет загрузки
<!-- Высокий приоритет: LCP-изображение (hero) -->
<img
src="hero-banner.jpg"
alt="Главный баннер"
fetchpriority="high"
width="1200"
height="600"
>
<!-- Низкий приоритет: некритичное изображение -->
<img
src="decoration.jpg"
alt=""
fetchpriority="low"
loading="lazy"
>
<!-- auto (по умолчанию): браузер решает сам -->
<img src="photo.jpg" alt="Фото" fetchpriority="auto">
Комбинация loading + fetchpriority
<!-- Hero-изображение: загрузить первым, с высоким приоритетом -->
<img
src="hero.jpg"
alt="Главный баннер"
loading="eager"
fetchpriority="high"
width="1200"
height="600"
>
<!-- Изображения ниже fold: lazy + auto -->
<img
src="feature-1.jpg"
alt="Функция 1"
loading="lazy"
width="400"
height="300"
>
<!-- Декоративное изображение: lazy + low -->
<img
src="pattern.jpg"
alt=""
loading="lazy"
fetchpriority="low"
>
Правила: когда НЕ использовать lazy
<!-- НЕ ставь lazy на LCP-изображение (hero, banner) -->
<!-- Плохо: -->
<img src="hero.jpg" loading="lazy" alt="Hero">
<!-- Хорошо: -->
<img src="hero.jpg" fetchpriority="high" alt="Hero">
<!-- НЕ ставь lazy на изображения above the fold -->
<!-- (те, что видны без прокрутки) -->
<!-- НЕ ставь lazy на все подряд -->
<!-- Первые 2-3 изображения -- eager (или без атрибута) -->
Правило: loading="lazy" только для изображений below the fold (ниже видимой области).
Как браузер определяет момент загрузки
Viewport (видимая область)
┌─────────────────────────┐
│ img (eager) -- загружен │
│ img (eager) -- загружен │
└─────────────────────────┘
↕ ~1250px (threshold)
┌─────────────────────────┐
│ img (lazy) -- загружается│ ← приблизился к viewport
└─────────────────────────┘
┌─────────────────────────┐
│ img (lazy) -- ждёт │ ← далеко от viewport
└─────────────────────────┘
Threshold (порог) зависит от:
- Скорости соединения (4G → раньше, 2G → позже)
- Браузера (каждый определяет свой threshold)
- Обычно ~1250px до viewport
Обязательно: width и height
<!-- Без width/height: layout shift при загрузке (CLS) -->
<img src="photo.jpg" loading="lazy" alt="Фото">
<!-- Браузер не знает размер → место не зарезервировано → сдвиг контента -->
<!-- С width/height: место зарезервировано -->
<img
src="photo.jpg"
loading="lazy"
alt="Фото"
width="400"
height="300"
>
<!-- Браузер вычисляет aspect ratio → резервирует место → нет сдвига -->
/* CSS aspect-ratio как альтернатива */
img {
width: 100%;
height: auto;
aspect-ratio: 4 / 3;
}
Lazy loading с picture и srcset
<picture>
<source type="image/avif" srcset="photo.avif">
<source type="image/webp" srcset="photo.webp">
<img
src="photo.jpg"
alt="Фото"
loading="lazy"
width="800"
height="600"
>
<!-- loading="lazy" на <img> внутри <picture> -->
</picture>
<!-- srcset + lazy -->
<img
src="photo-800.jpg"
srcset="photo-400.jpg 400w, photo-800.jpg 800w, photo-1200.jpg 1200w"
sizes="(max-width: 600px) 100vw, 50vw"
loading="lazy"
alt="Фото"
width="800"
height="600"
>
Intersection Observer (JavaScript fallback)
Для более тонкого контроля или поддержки старых браузеров:
// Кастомная lazy loading через Intersection Observer
const lazyImages = document.querySelectorAll('img[data-src]');
const observer = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
const img = entry.target;
img.src = img.dataset.src;
if (img.dataset.srcset) {
img.srcset = img.dataset.srcset;
}
img.removeAttribute('data-src');
observer.unobserve(img);
}
});
},
{ rootMargin: '200px' } // Начать загрузку за 200px до viewport
);
lazyImages.forEach((img) => observer.observe(img));
<!-- HTML для JS-подхода -->
<img
data-src="photo.jpg"
data-srcset="photo-400.jpg 400w, photo-800.jpg 800w"
src="placeholder.svg"
alt="Фото"
width="400"
height="300"
>
Частые ошибки
| Ошибка | Проблема | Решение |
|---|---|---|
loading="lazy" на LCP |
Замедляет главное изображение | fetchpriority="high" вместо lazy |
Нет width/height |
Layout shift (плохой CLS) | Указать размеры |
| Lazy на все изображения | Above-the-fold загружаются поздно | Lazy только для below-the-fold |
loading="lazy" на CSS background-image |
Атрибут не работает для CSS | Intersection Observer |
Нет alt |
Недоступно для SR | Описательный alt |
Практика
- Добавь
loading="lazy"ко всем изображениям ниже первого экрана - Убедись, что hero-изображение имеет
fetchpriority="high" - Проверь CLS: все lazy-изображения должны иметь
widthиheight - Открой DevTools → Network → отфильтруй Img → прокрути страницу и наблюдай загрузку
- Сравни LCP с lazy и без него на hero-изображении
Связанные темы
- Изображения -- базовый
<img> - picture и srcset -- адаптивные изображения
- viewport -- responsive design