Ленивая загрузка (lazy loading) — это стратегия отложенной загрузки ресурсов (в основном изображений и видео), которая загружает их только тогда, когда они действительно нужны: когда элемент появляется в видимой области (viewport) или приближается к ней. Определение в одно предложение: метод откладывания загрузки несущественных ресурсов до момента их отображения пользователю.
Ключевые эффекты:
Меньше запросов и меньшая нагрузка на сеть при первоначальной загрузке.
Быстрее отображение первого контента (First Contentful Paint).
Экономия трафика для мобильных пользователей.
Почему использовать ленивую загрузку
Более быстрая начальная загрузка страницы — пользователь видит контент быстрее и может начать взаимодействовать.
Улучшённая отзывчивость интерфейса при прокрутке — ресурсы подгружаются по ходу прокрутки.
Экономия трафика и ресурсов сервера (особенно при длинных страницах со множеством медиа).
Положительное влияние на SEO: скорость страницы — фактор ранжирования.
Important: Не все сценарии выигрывают от lazy loading. Для «above-the-fold» критичных изображений лучше загружать их сразу.
Быстрый пример: HTML с loading=”lazy”
Самый простой путь — использовать встроенный атрибут loading:
Атрибут loading=”lazy” сообщает браузеру отложить загрузку ресурса до тех пор, пока он не потребуется.
Улучшение: низкокачественный плейсхолдер и Intersection Observer
Нативный loading — отличное начало, но для большей совместимости и плавного перехода можно комбинировать низкокачественные плейсхолдеры (LQIP) и JavaScript на основе Intersection Observer. Идея: исходно загружается небольшое лёгкое изображение (webp/blur), а при появлении в viewport заменяем src на полноразмерный файл.
HTML-разметка с плейсхолдерами и data-src:
JavaScript-логика с Intersection Observer (работающая и в современных браузерах):
"use strict";
// Выбираем все изображения, помеченные loading="lazy"
const lazyImages = document.querySelectorAll('img[loading="lazy"]');
// Опции observer'а: немного заранее подгружаем (rootMargin) и порог видимости
const observerOptions = {
root: null, // viewport
rootMargin: '200px 0px', // подгружаем, когда изображение в 200px от viewport
threshold: 0.01 // считаем элемент пересечённым при минимальной видимости
};
const observer = new IntersectionObserver((entries, obs) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const img = entry.target;
if (img.dataset && img.dataset.src) {
img.src = img.dataset.src; // заменяем плейсхолдер на полноценное изображение
// Можно дополнительно удалить data-src или добавить класс loaded
img.removeAttribute('data-src');
}
obs.unobserve(img); // перестаём наблюдать за загруженным элементом
}
});
}, observerOptions);
// Запускаем наблюдение для каждого изображения
lazyImages.forEach(img => {
observer.observe(img);
});
Советы:
Используйте rootMargin, чтобы подгружать изображения заранее и избежать «вспышек» при быстром скролле.
threshold=0.01 позволяет начинать загрузку при очень малой доле видимости.
После загрузки снимайте наблюдение для экономии ресурсов.
Обработка фоновых изображений и элемент div
Для фоновых картинок (background-image) также можно использовать Intersection Observer. Пример:
Контент
const lazyBackgrounds = document.querySelectorAll('.lazy-bg');
lazyBackgrounds.forEach(bg => {
observer.observe(bg);
});
// В callback observer'а при isIntersecting заменить background-image на data-bg
Адаптивные изображения (srcset и picture)
Для экономии трафика стоит отдавать оптимизированные варианты изображений через srcset и picture. Пример сочетания srcset и lazy loading:
В JavaScript при lazy-замене нужно копировать data-srcset в srcset, а не только src.
Взаимодействие с SEO и доступностью
Доступность: всегда указывайте alt с описанием изображения или пустой alt=”” для декоративных элементов.
SEO: поисковики индексируют ленивые изображения, если они появляются в DOM и имеют корректные атрибуты; используйте server-side rendering для критичных элементов.
Prerender/SSR: для страниц с критичным «above-the-fold» контентом отдавайте основные изображения с сервера, а остальное можно лениво подгружать.
Notes: Для экранных читалок alt важен — он описывает визуальный контент.
Совместимость с браузерами и полифиллы
Нативный атрибут loading поддерживают большинство современных браузеров, но не все старые версии.
Intersection Observer поддерживается в современных браузерах; для старых потребуется полифилл.
Рекомендуется проверять поддержку через Can I Use и при необходимости подключать полифилл с CDN для IE11/старых Android-браузеров.
Тестирование и критерии приёмки
Критерии приёмки:
Изображения above-the-fold загружаются сразу без значимой задержки.
Изображения, появляющиеся при прокрутке, подгружаются своевременно и без визуальных «миганий».
Для каждого img есть корректный alt.
После перехода на высококачественный src, исходный low-src не остается в data-src.
Нет ошибок в консоли (CORS, 404 и т.д.).
Тест-кейсы:
Открыть страницу с медленным соединением — проверить, что начальная загрузка стала быстрее.
Быстро проскроллить страницу — убедиться, что изображения не «прыгают» и подгружаются заранее.
Отключить JS — убедиться, что критичные изображения всё ещё видимы (грейсфул деградация).
Тестировать с экранным читалкой — alt читается корректно.
Проверить в старых браузерах с подключённым полифиллом.
Когда ленивый подход не подходит
Маленькие страницы с 1–2 изображениями: выигрыш минимален, усложнение не нужно.
Критичные визуальные элементы в верхней части страницы: задержка загрузки ухудшит UX.
Когда внешняя аналитика или бот-парсеры требуют немедленной загрузки всех изображений.
Альтернативные подходы:
Использовать CDN с адаптивной трансформацией изображений (автоматически отдаёт нужный размер).
Библиотеки: lazysizes, lozad.js — готовые решения с полифиллами и дополнительным функционалом.
Руководство внедрения (мини-SOP)
Проинвентаризируйте изображения страницы: пометьте критичные для initial render.
Для некритичных добавьте loading=”lazy” и, по желанию, data-src с плейсхолдером.
Подготовьте LQIP (маленькие webp/blur) для плавного UX.
Добавьте Intersection Observer с rootMargin для предзагрузки.
Тестируйте на разных устройствах и сетях.
Отслеживайте метрики: FCP, LCP, CLS, TTFB.
При необходимости добавьте полифилл для старых браузеров.
Чек-лист для релиза
Критичные изображения — не ленивые.
Все img имеют alt.
Нет 404 у изображений.
JS-переключение src работает для srcset/picture.
Произведено кроссбраузерное тестирование.
Эвристики и ментальные модели
Правило 80/20: оптимизируйте первые 20% видимого контента — они дают 80% первого впечатления.
Предзагрузка: лучше подгружать немного раньше (rootMargin), чем догонять загрузку уже в поле зрения.
Плейсхолдеры улучшают восприятие скорости даже при длительной загрузке.
Отладка и распространённые проблемы
Проблема: изображение не загружается при прокрутке. Причина: observer.observe не вызван или querySelector не нашёл элементов.
Проблема: быстрое прокручивание вызывает «пустые» места. Решение: увеличить rootMargin или использовать eager загрузку для блоков, где пользователь часто останавливается.
Проблема: Cumulative Layout Shift (CLS) из-за отсутствия указанных размеров. Решение: всегда задавайте width/height или используйте aspect-ratio/placeholders.
Краткое резюме
Ленивая загрузка изображений — простой и эффективный способ улучшить скорость сайта и экономию трафика. Начните с native loading=”lazy”, затем добавьте Intersection Observer для более контролируемой подгрузки, LQIP и поддержку srcset для адаптивности. Обязательно тестируйте на реальных устройствах и учитывайте доступность.
Часто задаваемые вопросы
Работает ли loading=”lazy” во всех браузерах?
Не во всех. Большинство современных браузеров поддерживают этот атрибут, но для старых версий требуется полифилл.
Нужно ли использовать low-quality placeholders?
Не обязательно, но они заметно улучшают визуальное восприятие при медленной сети и уменьшают эффект «прыжка» изображения.