Как реализовать ленивую загрузку изображений без использования библиотек
Что проверяют
Знаете ли вы, что браузер давно умеет это сам, и понимаете ли, как устроен ручной вариант на IntersectionObserver. Следом спрашивают, почему после ленивой загрузки страница прыгает и какую картинку лениво грузить нельзя.
Как отвечать
Первым назовите нативный атрибут loading="lazy" у <img> и <iframe>. Он поддерживается всеми современными браузерами, не требует JS, и браузер сам решает, насколько заранее начать загрузку. Для большинства задач это и есть ответ «без библиотек».
Ручная реализация нужна, когда требуется контроль: свой отступ, анимация появления, фоновые картинки в CSS.
- разметка: реальный адрес в
data-src(иdata-srcset, если есть адаптивные версии), вsrcлёгкая заглушка или ничего; - наблюдатель: один
IntersectionObserverна все изображения сrootMargin: '200px 0px', чтобы загрузка начиналась до появления картинки на экране; - колбэк: при
isIntersectingпереносимdata-srcвsrcи снимаем наблюдение черезunobserve, а класс-заглушку убираем по событиюload, не сразу; - место под картинку: атрибуты
widthиheightили CSSaspect-ratio, тогда браузер резервирует блок заранее и сдвига макета (CLS) нет; - ошибки: обработчик
errorподставляет запасное изображение.
Отдельно скажите про LCP. Главное изображение первого экрана не делают ленивым, наоборот, ему ставят fetchpriority="high", иначе метрика ухудшается.
Старый подход через событие scroll и getBoundingClientRect упомяните как исторический: он заставляет пересчитывать раскладку во время прокрутки и требует троттлинга.
Где спотыкаются
Делают ленивыми все изображения подряд, включая баннер наверху. Страница визуально грузится медленнее, чем без всякой оптимизации.
Не задают размеры. Картинки подгружаются и сталкивают текст вниз прямо под пальцем пользователя.
Создают отдельный IntersectionObserver на каждое изображение или забывают unobserve. На длинной ленте это лишняя работа и ссылки на уже удалённые узлы.