Использование ленивой загрузки изображений для улучшения производительности веб-проектов — востребованная техника оптимизации. Всё дело в том, что изображения — это «тяжёлые» ресурсы, которыми переполнены современные веб-сайты. Мы уже кое-что об этом публиковали. Здесь можно почитать о том, что дала ленивая загрузка сайту Walmart, и узнать о том, как пользоваться IntersectionObserver в React-проектах. Вот статья об оптимизация статических сайтов. Вот недавний материал о реализации ленивой загрузки средствами браузера.



Сегодня мы представляем вашему вниманию перевод статьи, в которой использование API IntersectionObserver рассмотрено на примере простой веб-страницы. Этот материал рассчитан на начинающих программистов.

Что такое ленивая загрузка изображений?


При анализе производительности приложений на первый план выступают два показателя — время до первой интерактивности (Time To Interactive) и потребление ресурсов (Resources Consumption). С ними неизбежно придётся столкнуться тем, кто занимается веб-разработкой. Кроме того, проблемы с приложениями могут возникать не только из-за того, что они долго готовятся к работе или потребляют слишком много ресурсов. Но, в любом случае, очень важно как можно раньше находить источники этих проблем и стремиться к тому, чтобы проблемы даже и не возникали.

Использование технологий ленивой загрузки изображений в веб-приложениях позволяет свести к минимуму риск возникновения проблем с производительностью. А именно, если проанализировать ленивую загрузку с точки зрения её влияния на время приведения страницы в рабочее состояние и потребление ресурсов, у нас получится следующее:

  1. Время до первой интерактивности. Это — время, которое нужно веб-приложению для загрузки и приведения интерфейса в состояние, пригодное для работы с ним пользователя. Ленивая загрузка (причём, речь идёт не только об изображениях) оптимизирует время отклика приложений благодаря технологиям разделения кода и загрузки только того, что нужно конкретной странице, или того, что нужно в некий конкретный момент времени.
  2. Потребление ресурсов. Люди — существа нетерпеливые. Если веб-сайту нужно больше 3 секунд на то, чтобы загрузиться, 70% пользователей с такого сайта уходят. Веб-приложения не должны загружаться так долго. Ленивая загрузка позволяет уменьшить объём ресурсов, необходимых для работы страниц. Речь, например, может идти о том, что код некоего проекта разбивается на фрагменты, которые загружаются только на тех страницах, которые в них нуждаются. В результате растёт производительность сайта и снижается потребление системных ресурсов.

Итак, технологии ленивой загрузки ускоряют приложения, сокращая время их загрузки и приведения в рабочее состояние. Достигается это благодаря тому, что ресурсы загружаются только тогда, когда они нужны.

Вот пара преимуществ, которые даёт веб-проектам ленивая загрузка:

  • Страницы быстрее загружаются и приходят в рабочее состояние.
  • При первоначальной загрузке страниц приходится передавать и обрабатывать меньший объём данных.

Ленивая загрузка изображений с использованием API IntersectionObserver


Если подумать о том, как происходит загрузка веб-страниц, становится ясно, что нет смысла загружать изображения или другие ресурсы, которые не видны пользователю. При использовании ленивой загрузки сначала загружаются изображения, которые находятся в видимой области страницы. Затем, по мере того, как пользователь прокручивает страницу, загружаются другие изображения, попадающие в видимую область страницы.

Рассмотрим пример.


Страница и её видимая область

Взгляните на предыдущий рисунок. Тут можно видеть браузер и загружаемую в него веб-страницу. Изображения #IMG_1 и #IMG_2 находятся в области видимости страницы. Это означают, что они видимы пользователю и находятся в границах той области окна браузера, которая выводится на экран.

Нельзя признать идеальным такой порядок работы со страницей, когда при её загрузке сразу же загружаются и изображения #IMG_1, #IMG_2, #IMG_3 и #IMG_4. Пользователю видны лишь #IMG_1 и #IMG_2, а #IMG_3 и #IMG_4 от него скрыты. Если при загрузке страницы загрузить первое и второе изображения, а третье и четвёртое не загружать — это могло бы оказать позитивное влияние на производительность сайта. А именно, речь идёт о следующем. Когда пользователь прокручивает страницу так, что видимым становится третье изображение — оно загружается. Если прокрутка продолжается и видимым становится четвёртое изображение — оно тоже загружается.


Прокрутка страницы и загрузка изображений

Теперь, когда мы выяснили, как именно мы хотим работать с изображениями, зададимся вопросом о том, как узнать о том, что некий элемент страницы попадает в её видимую область. В современных браузерах имеется API, позволяющий программисту узнать о том, когда некая область страницы становится видимой. Речь идёт об API IntersectionObserver. Он позволяет, пользуясь асинхронными механизмами, организовать наблюдение за элементами и получать уведомления в тех случаях, когда элемент пересекает область видимости документа или пересекает границу другого элемента.

Для того чтобы настроить ленивую загрузку изображений, нам для начала понадобится подготовить шаблонный код элемента, который будет использоваться для описания изображений. Вот он:

<img class="lzy_img" src="lazy_img.jpg" data-src="real_img.jpg" />

Класс позволяет идентифицировать элемент как изображение, к которому будет применяться ленивая загрузка. Атрибут src позволяет вывести изображение-заполнитель до вывода реального изображения. В data-src хранится адрес реального изображения, которое будет загружено при попадании элемента в видимую область страницы.

Теперь напишем код, реализующий ленивую загрузку. Как уже было сказано, для обнаружения момента попадания изображения в область просмотра страницы мы будем пользоваться API IntersectionObserver.

Для начала создадим экземпляр IntersectionObserver:

const imageObserver = new IntersectionObserver(...);

Конструктор IntersectionObserver принимает функцию с двумя параметрами. Один из них хранит массив, состоящий из элементов, за которыми нужно организовать наблюдение, другой — экземпляр IntersectionObserver. Выглядит это так:

const imageObserver = new IntersectionObserver((entries, imgObserver) => {
    entries.forEach((entry) => {
        //...
    })
});

В коде функции осуществляется проверка того, пересекают ли изображения, представленные элементами массива entries, область просмотра. Если это так — то в атрибут src соответствующего изображения записывается то, что было в его атрибуте data-src.

const imageObserver = new IntersectionObserver((entries, imgObserver) => {
    entries.forEach((entry) => {
        if(entry.isIntersecting) {
            const lazyImage = entry.target
            lazyImage.src = lazyImage.dataset.src
        }
    })
});

Тут мы проверяем, с помощью условия if(entry.isIntersecting) {...}, пересекает ли элемент область просмотра браузера. Если это так — мы сохраняем элемент img в константе lazyImage. Затем записываем в его атрибут src то, что было в его атрибуте data-src. Благодаря этому изображение, адрес которого хранится в data-src, загружается и выводится на экран. В браузере это выглядит как замена изображения-заполнителя, lazy_img.jpg, на реальное изображение.

Теперь нужно воспользоваться методом .observe() нашего экземпляра IntersectionObserver для того чтобы начать наблюдение за интересующими нас элементами:

imageObserver.observe(document.querySelectorAll('img.lzy_img'));

Здесь мы выбираем из документа все элементы img с классом lzy_img командой document.querySelectorAll('img.lzy_img') и передаём их методу .observe(). Он, получив список элементов, запускает процесс наблюдения за ними.

Для того чтобы испытать этот пример начнём с инициализации Node.js-проекта:

mkdir lzy_img
cd lzy_img
npm init -y

Теперь создадим в папке lzy_img файл index.html:

touch index.html

Добавим в него следующий код:

<html>
<title>Lazy Load Images</title>
<body>
    <div>
        <div style="">
            <img class="lzy_img" src="lazy_img.jpg" data-src="img_1.jpg" />
            <hr />
        </div>
        <div style="">
            <img class="lzy_img" src="lazy_img.jpg" data-src="img_2.jpg" />
            <hr />
        </div>
        <div style="">
            <img class="lzy_img" src="lazy_img.jpg" data-src="img_3.jpg" />
            <hr />
        </div>
        <div style="">
            <img class="lzy_img" src="lazy_img.jpg" data-src="img_4.jpg" />
            <hr />
        </div>
        <div style="">
            <img class="lzy_img" src="lazy_img.jpg" data-src="img_5.jpg" />
            <hr />
        </div>
    </div>
    <script>
        document.addEventListener("DOMContentLoaded", function() {
            const imageObserver = new IntersectionObserver((entries, imgObserver) => {
                entries.forEach((entry) => {
                    if (entry.isIntersecting) {
                        const lazyImage = entry.target
                        console.log("lazy loading ", lazyImage)
                        lazyImage.src = lazyImage.dataset.src
                    }
                })
            });
            const arr = document.querySelectorAll('img.lzy_img')
            arr.forEach((v) => {
                imageObserver.observe(v);
            })
        })
    </script>
</body>
</html>

Можно заметить, что тут описаны 5 изображений, в которых при загрузке выводится заполнитель lazy_img.jpg. В атрибуте data-src каждого из них содержатся сведения о реальных изображениях. Вот список имён изображений, используемых в проекте:

lazy_img.jpg
img_1.jpg
img_2.jpg
img_3.jpg
img_4.jpg
img_5.jpg

Все эти изображения вам надо создать самостоятельно, приведя папку проекта к виду, показанному на следующем рисунке.


Папка проекта

В моём случае файл lazy_img.jpg подготовлен средствами Windows Paint, а реальные изображения (img_*.jpg) взяты с pixabay.com.

Обратите внимание на то, что в коллбэке, использованном при создании экземпляра IntersectionObserver, есть вызов console.log(). Это позволит нам узнать о выполнении операций по загрузке разных изображений.

Теперь, чтобы обслуживать index.html, воспользуемся пакетом http-server:

npm i http-server

Добавим свойство start в раздел scripts файла package.json:

"scripts": {
    "start": "http-server ./"
}

После этого выполним в терминале команду npm run start.

Теперь открываем браузер и переходим по адресу 127.0.0.1:8080. Наша страница index.html в самом начале будет выглядеть примерно так, как показано на следующем рисунке.


Страница без реальных изображений

Можно заметить, что там, где должны быть реальные изображений, сейчас выводятся лишь местозаполнители. Тут мы исходим из предположения, в соответствии с которым первое изображение находится в области просмотра, поэтому браузер приступает к его загрузке. В результате страница теперь будет выглядеть так.


Браузер загрузил первое изображение

Другие изображения пока не загружены. Они ещё не попали в область просмотра.

Если прокручивать страницу, наблюдая за консолью, можно заметить одну проблему. Заключается она в том, что при прокрутке одного и того же изображения в окне просмотра браузер пытается загрузить его несколько раз.


Многократная загрузка одного и того же изображения

Это совсем не то, что нам нужно. Подобное приводит к ненужной трате системных ресурсов и ухудшает производительность проекта.

Для того чтобы решить эту проблему нам нужно убирать элемент img, настоящее изображение для которого уже загружено, из списка элементов, за которыми наблюдает наш экземпляр IntersectionObserver. Также нам нужно убирать класс lzy_img из этого элемента. Вот как это выглядит в отредактированном коде функции обратного вызова:

<script>
    document.addEventListener("DOMContentLoaded", function() {
        const imageObserver = new IntersectionObserver((entries, imgObserver) => {
            entries.forEach((entry) => {
                if (entry.isIntersecting) {
                    const lazyImage = entry.target
                    console.log("lazy loading ", lazyImage)
                    lazyImage.src = lazyImage.dataset.src
                    lazyImage.classList.remove("lzy_img");
                    imgObserver.unobserve(lazyImage);
                }
            })
        });
        const arr = document.querySelectorAll('img.lzy_img')
        arr.forEach((v) => {
            imageObserver.observe(v);
        })
    })
</script>

После загрузки реального изображения соответствующего элемента img удаляется его класс lzy_img и элемент исключается из списка элементов, за которыми наблюдает IntersectionObserver.

Вот готовый код примера:

<html>
<title>Lazy Load Images</title>
<body>
    <div>
        <div style="">
            <img class="lzy_img" src="lazy_img.jpg" data-src="img_1.jpg" />
            <hr />
        </div>
        <div style="">
            <img class="lzy_img" src="lazy_img.jpg" data-src="img_2.jpg" />
            <hr />
        </div>
        <div style="">
            <img class="lzy_img" src="lazy_img.jpg" data-src="img_3.jpg" />
            <hr />
        </div>
        <div style="">
            <img class="lzy_img" src="lazy_img.jpg" data-src="img_4.jpg" />
            <hr />
        </div>
        <div style="">
            <img class="lzy_img" src="lazy_img.jpg" data-src="img_5.jpg" />
            <hr />
        </div>
    </div>
    <script>
        document.addEventListener("DOMContentLoaded", function() {
            const imageObserver = new IntersectionObserver((entries, imgObserver) => {
                entries.forEach((entry) => {
                    if (entry.isIntersecting) {
                        const lazyImage = entry.target
                        console.log("lazy loading ", lazyImage)
                        lazyImage.src = lazyImage.dataset.src
                        lazyImage.classList.remove("lzy_img");
                        imgObserver.unobserve(lazyImage);
                    }
                })
            });
            const arr = document.querySelectorAll('img.lzy_img')
            arr.forEach((v) => {
                imageObserver.observe(v);
            })
        })
    </script>
</body>
</html>

Итоги


Мы рассмотрели простейший пример реализации системы ленивой загрузки изображений. Но IntersectionObserver позволяет создавать и достаточно сложные схемы ленивой загрузки. Если вам эта тема интересна — рекомендуем взглянуть на публикации, упомянутые в начале материала и почитать документацию.

Уважаемые читатели! Есть ли у вас примеры улучшения производительности веб-сайтов после внедрения системы ленивой загрузки изображений?

Комментарии (22)


  1. PaulZi
    31.05.2019 14:18
    +1

    Спасибо за IntersectionObserver!
    Дополню поддержкой браузерами:
    https://caniuse.com/#search=IntersectionObserver


  1. dom1n1k
    31.05.2019 15:15

    IntersectionObserver API — штука очень спорной необходимости. На первый взгляд — ну как же, lazy-load картинок, оптимизация, все дела! Я сам писал библиотеку для ленивой загрузки картинок и она даже довольно неплохо работала (не опубликована).
    Но! Всё разбивается о скалы picture/source/srcset. Как только нужно делать адаптивность по-взрослому, под все устройства/браузеры/ретины, реализовать ленивую загрузку становится очень-очень сложно.
    Вывод прост: полноценная ленивая загрузка должна реализовываться нативно в браузере — и Хром сейчас идет в правильном напрвлении.


    1. demimurych
      31.05.2019 20:46
      -1

      Вы явно чем то не разобрались.
      srcset и sizes + обсервер апи — наоборот сделали лейзилоад возможным в серьезных проектах., которым важен трафик из органического поиска.


      picture же совместо с source это баловство, которое не имеет никакого отношения к сложным проектам.
      Никто в здравом уме не будет решать проблемы поддержки форматов на стороне клиента, где каждый лишний узел в дом дереве на вес золота. Все это решается на уровне сервера.


      1. dom1n1k
        31.05.2019 23:44

        Непонятно, что сделал возможным IntersectionObserver? Он ведь не дает ничего принципиально нового, что нельзя было бы реализовать руками. Да, с ним удобнее, надёжнее, меньше возможностей накосячить — это всё верно и хорошо.
        Но ведь атрибуты src/data-src всё равно точно так же меняются местами. В отличие от нативного варианта. (Хотя справедливости ради, библиотеки дают шире возможности).

        По поводу source. Даже если не вступать в спор, на чьей стороне лучше решать вопрос с форматами, у него есть другой интересный атрибут — media. И он бывает даже более важен для нрмальной адаптивности, чем srcset/sizes.


        1. demimurych
          01.06.2019 00:12

          Непонятно, что сделал возможным IntersectionObserver? Он ведь не дает ничего принципиально нового, что нельзя было бы реализовать руками. Да, с ним удобнее, надёжнее, меньше возможностей накосячить — это всё верно и хорошо.

          Тем что раньше, делая это руками, Вы нагружали CPU тысячами срабатываний вашего обработчика который висел на scroll, resize, и кучи других событий. Вплоть до таймера.
          Сейчас все это делает сам браузер на своем уровне. И делает это на 99.9% хорошо. Покажите мне программиста который не хочет избавиться от лишней логики при этом еще и разгружая ЦП.

          Но ведь атрибуты src/data-src всё равно точно так же меняются местами.

          Не меняются. То что написано в статье это из раздела вредных советов. Так делать ни в коем случае нельзя. Сейчас правильный лейзилоад оперирует исключительно атрибутом srcset

          Важно понимать, что в случае если указан атрибут srcset то браузер показывает то, что в этом атрибуте, вне зависимости от того, что находится в атрибуте src. Именно этот факт сделал LazyLoad применимым. Теперь есть гарантии, что поисковый робот проиндексирует изображения, а не заглушки. (в src всегда находится ссылка на изображение отвечающее контенту)

          В отличие от нативного варианта.

          Нативный вариант это безусловно прекрасно. При условии если дадут контролировать логику его работы. Сейчас это жалкая подделка для тех, кто может только прописать лишний атрибут в теге.
          lazyload не ограничивается работой только с изображениями. Это инструмент который позволяет решать массу задач.

          Даже если не вступать в спор, на чьей стороне лучше решать вопрос с форматами,

          Здесь нет перемета спора. Если сервер, обеспечивающий отдачу статики, находится под вашим контролем, то именно он должен принимать решение какую именно статику нужно отдать на запрос бразуера. Браузеры давно научились передавать в заголовках
          достаточно информации для того, чтобы сервер мог однозначно принять решение о том, какой формат того же изображения будет выгоднее всего.

          Исключением является только тот редкий случай, когда статику подгружают с чужого сервера, поведением которого невозможно управлять.

          у него есть другой интересный атрибут — media. И он бывает даже более важен для нормальной адаптивности, чем srcset/sizes.

          Каким это образом атрибут media вдруг оказался важнее sizes, когда в sizes можно указывать media запросы да еще и под каждый конкретный файл?

          Но даже если есть какая то особая миссия именно у одного глобального медиа запроса на это изображение, тоже самое можно сделать через медиа запрос привязанный к стилю этого изображения.


          1. dom1n1k
            01.06.2019 01:03

            Вы нагружали CPU тысячами срабатываний вашего обработчика
            Разумеется, нужно делать тротлинг, и нормальные библиотеки его делают. Это несколько строк кода, есть готовые реализации.
            Я ж не спорю, что с новым апи удобнее. Но ничего принципиально нового, что раньше ну вот никак не получалось, он не дал.
            Сейчас правильный лейзилоад оперирует исключительно атрибутом srcset
            Смысл абзаца я понял. Но не очень понятно, а что раньше мешало в атрибуте src держать не какую-то формальную заглушку, а LQIP-версию той же контентной картинки? Я именно так и делал. Опять же ничего принципиально нового.
            lazyload не ограничивается работой только с изображениями.
            Это да. Например, моя реализация умела лениво навешивать стили или вводить дополнительные временнЫе задержки.
            Здесь нет перемета спора.
            Его действительно нет в том смысле, что оба подхода вполне имеют право на существование в зависимости от ситуации. Ни про какой нельзя однозначно сказать, что он плох.
            в sizes можно указывать media запросы да еще и под каждый конкретный файл?
            Можно, но это верный способ взорвать мозг даже себе :) Не говоря уж о тех, кто будет читать код впоследствии. Проблема атрибутов srcset/sizes в том, что между файлами и размерами не устанавливается взаимно-однозначного соответствия.
            Пример из блога Оперы
            <img
            sizes="(max-width: 30em) 100vw, (max-width: 50em) 50vw, calc(33vw - 100px)"
            srcset="swing-200.jpg 200w, swing-400.jpg 400w, swing-800.jpg 800w, swing-1600.jpg 1600w"
            src="swing-400.jpg" alt="Kettlebell Swing">

            Что сейчас должно загрузиться?! Да черт его знает.
            Возможно, пример переусложнён, но потенциал для «творчества» демонстрирует.
            dev.opera.com/articles/native-responsive-images


            1. demimurych
              01.06.2019 01:32

              Смысл абзаца я понял. Но не очень понятно, а что раньше мешало в атрибуте src держать не какую-то формальную заглушку, а LQIP-версию той же контентной картинки? Я именно так и делал. Опять же ничего принципиально нового.

              Тем что Бот должен видеть картинку в максимальном качестве.
              Вы как и большинство верстальщиков/веб-программистов забываете, что тег IMG это семантический тег, который наряду с другими семантическими тегами вводили для того чтобы описывать суть контента на странице.

              Он несет смысловую нагрузку (изображение отвечающее контексту), и прямо влияет на ранжирование проекта в поисковой выдаче.

              Это да. Например, моя реализация умела лениво навешивать стили или вводить дополнительные временнЫе задержки.

              Какая бы у Вас не была реализация, ее применение ограничено тем кругом задач, где не важна работа поисковых роботов, или любых других роботов опирающихся на семантику по причине того, что ваш src равный пустоте или заглушке, будет интерпретирован роботом именно как пустота и заглушка.

              Его действительно нет в том смысле, что оба подхода вполне имеют право на существование в зависимости от ситуации. Ни про какой нельзя однозначно сказать, что он плох.

              Неверно. Десятки source это всегда плохо. В силу двух критически важных факторов: создание лишней нагрузки на браузер и раздувания ДОМ дерева, прямо влияющего как на работу JS в случае оперирования с ДОМ так и на обобьем потребления памяти. Не говоря уже о рендере.

              Можно, но это верный способ взорвать мозг даже себе :) Не говоря уж о тех, кто будет читать код впоследствии. Проблема атрибутов srcset/sizes в том, что между файлами и размерами не устанавливается взаимно-однозначного соответствия.

              Это уже вопрос квалификации.

              А когда в дело вступают сложные комбинации размеров/пропорций/кадрирования и тп. — получается всё очень запутанно и непонятно (во всяком случае для меня).

              Не получается. Даже если Вам трудно воспринимать такие медиа выражения, вы можете разбить это все на обычные классы которые обернуть в те же медиа.

              И ко всему прочему, браузер имеет некоторую свободу при интерпретации атрибута sizes.

              Нет не имеют. sizes всегда интерпретируется однозначно. Поведение которое выбирает браузер в зависимости от ситуации (канал, дпи, разрешние) может варьироваться. И это правильно, потому что человеку сидящему с ретина монитором на 2g канале нет никакого смысла грузить 3x изображение. Он просто не дождется его загрузки.


              1. dom1n1k
                01.06.2019 02:36

                Тем что Бот должен видеть картинку в максимальном качестве
                Я понимаю семантическое значение img. Я не понимаю, в чем принципиальная разница между ситуациями подмены src и srcset.
                Если мы решили экономить трафик, то в src в любом случае будет небольшая (но осмысленная!) заглушка.
                Если же у нас в src картинка максимального качества (для бота), то причем тут ленивая загрузка, если эта большая картинка будет всё равно загружена? Ведь она будет загружена? Потому что srcset у нас пустой до момента, когда элемент въедет во вьюпорт.
                Нет не имеют. sizes всегда интерпретируется однозначно
                В спецификации написано, что все эти атрибут имеют информационный характер, и юзер-агент может выбирать любой вариант исходя из своих соображений.
                Например, если я напишу:
                [img src="0.jpg" srcset="1.jpg 1000w, 2.jpg 2000w" sizes="1500px"]
                Браузер вправе выбрать любой из вариантов хотя бы потому, что 1500 ровно посередине между 1000 и 2000.
                На практике браузеры обычно выбирают большее значение. Но я как-то натыкался на случай, где Хром и ФФ делали разный выбор в пограничных значениях (к сожалению, уже не воспроизведу).


                1. demimurych
                  01.06.2019 05:08

                  Если же у нас в src картинка максимального качества (для бота), то причем тут ленивая загрузка, если эта большая картинка будет всё равно загружена

                  Не будет. Вы не внимательно читаете то что я вам пишу с самого начала.

                  Изображение размечается как содержащее атрибут src с максимальным качеством И атрибут srcset содержащий необходимые для отображения на текущий момент файлы.

                  Браузер никогда ничего не загрузит из src до тех пор пока есть srcset.

                  Чтобы в нем не содержалось.

                  Соответственно, в рамках LazyLoad мы имеем изображение у которого атрибут src с ссылкой на файл самого высокого качества, srcset содержащий единственный урл на заглушку,
                  и какой-нибудь data-srcset с набором файлов для нашего изображения.

                  Попали в область просмотра, заменили текущий srcset на data-scrset.

                  В спецификации написано, что все эти атрибут имеют информационный характер, и юзер-агент может выбирать любой вариант исходя из своих соображений.
                  Например, если я напишу:

                  В спецификации пишется о том, что атрибут srcset как и sizes содержат необходимы инструкции на основании которых браузер принимает решение о том, какой из файлов максимально близко отвечает текущей ситуации.

                  То что Вы задали два правила которые которые находятся на пересечении двух множеств событий — это лично Ваша проблема. А не проблема выдуманной Вами неоднозначности поведения браузера.

                  Задавайте правила без избыточности и у Вас не будет никаких проблем.

                  На практике браузеры обычно выбирают большее значение.

                  Ну что тут скажешь. Попрактикуйтесь побольше.


                  1. dom1n1k
                    01.06.2019 09:48

                    По src/srcset понятно. Но есть один нюанс — вы заставите юзеров старых браузеров качнуть эту самую картинку максимального качества. А ведь обычно люди со старыми браузерами сидят на слабом железе.

                    Задавайте правила без избыточности и у Вас не будет никаких проблем.
                    В общем случае это невозможно. Потому что это только в стерильном примере я написал фиксированное значение в px. А в реальности там будут какие-нибудь vw или calc.
                    А главное, неизбежно пересекающиеся медиа-запросы — размеры экранов, ориентации экранов, плотности пикселей. И там будет избыточность. Если бы нужно было делать адаптивность строго по одному параметру (ширине или плотности пикселей) — то тогда проблемы действительно нет.


                    1. demimurych
                      02.06.2019 05:19

                      По src/srcset понятно. Но есть один нюанс — вы заставите юзеров старых браузеров качнуть эту самую картинку максимального качества. А ведь обычно люди со старыми браузерами сидят на слабом железе.

                      Разговор начался с того, что
                      srcset и sizes + обсервер апи — наоборот сделали лейзилоад возможным в серьезных проектах., которым важен трафик из органического поиска.


                      Что определяет условия в которых мы ведем разговор. А именно существование srcset, которые позволили делать LazyLoad который никаким образом не повлияет на индексацию поисковыми роботами.

                      Применения других LazyLoadov до того момента фактически убивало проект в поиске, в любой конкурентной нише.

                      С появлением srcset стал возможен выбор — увеличить аудиторию за счет тех кто не уйдут с тормозящего сайта, но потерять тех кто сидит на старом бразуере, или потерять первых но спасти вторых. При этом сохранив в обоих случаях корректную индексацию.

                      srcset сейчас это 93% всех браузеров в мире. Дальше вопрос бизнеса, если ты теряешь 7% от тех кто пользуется устаревшими браузерами, коменисруешь ли ты это ростом за счет оставшейся аудитории.

                      В общем случае это невозможно. Потому что это только в стерильном примере я написал фиксированное значение в px. А в реальности там будут какие-нибудь vw или calc.
                      А главное, неизбежно пересекающиеся медиа-запросы — размеры экранов, ориентации экранов, плотности пикселей. И там будет избыточность. Если бы нужно было делать адаптивность строго по одному параметру (ширине или плотности пикселей) — то тогда проблемы действительно нет.


                      Обожаю людей с развитым воображением. Это всегда в плюс к человеку. Даже когда он увлекается.

                      НЕТ никакой разницы какие правила вы используете и какие единицы для этого используете.
                      source от sizes отличается только одним — глобальным медиа правилом у тега source, которое можно либо уложить в рамках sizes, либо повторить его (медиа запрос) в стилях закрепив под него класс (), примененный к тому же изображению ( )

                      Хотите сказать что это не так?
                      Приведите живой пример где бы было невозможно повторить поведение sources их не используя.


                      1. dom1n1k
                        02.06.2019 20:46

                        Я не говорил, что повторить поведение source нельзя. Слово «невозможно» относилось ко фразе «задавайте правила без избыточности».

                        Я говорил, что поскольку source позволяет установить взаимно-однозначное соответствие между файлом и медиа-запросом, то при их большом количестве структура становится гораздо более читаемой и понятной. И плевать на несколько дополнительных узлов в доме — разница незначительна, в отличие от удобства.


                      1. dom1n1k
                        03.06.2019 11:45

                        А хотя нашелся пример. Да, он использовался на практике, хотя и редко.
                        Я выше упоминал о разных ориентациях экрана — для десктопа и телефона показываются фотки с разным кадрированием.
                        Ну и плюс конечно размеры тоже, но тут они убраны для наглядности.

                        <picture>
                        	<source srcset="horizontal.jpg" media="(orientation: landscape)">
                        	<source srcset="vertical.jpg" media="(orientation: portrait)">
                        	<img src="fallback.jpg">
                        </picture>

                        Логично: sizes, как и намекает имя атрибута, справляется если картинка одна, просто разных размеров. Но как только появляются содержательно разные картинки, нужен source.


  1. noodles
    31.05.2019 16:17

    Интересуюсь, какая есть необходимость в использовании ленивой загрузки картинок? Ведь картинки — это неблокирующие ресурсы для браузера. Грузятся себе в фоне, и хорошо, отрисовке dom-а не мешают. Зато при скроле к картинке — она уже вероятно будет загружена, не нужно будет ждать.

    Если ленивая загрузка решает случай, когда на одной html-ине сотни больших картинок, так разве не пагинация эту проблему должна решать?


    1. dom1n1k
      31.05.2019 16:41
      +1

      1. Экономия трафика (причем и клиентского, и серверного).
      2. Экономия памяти устройства (актуально для мобильников).
      3. Возможность управлять порядком загрузки (например, сначала загрузить контентные картинки, а уж потом размытый декоративный фон).

      Основная суть ленивой загрузки не в том, чтобы загрузить картинку чуть попозже — а в том, чтобы загрузить её только при реальной необходимости. Потому что пользователь может вообще никогда до неё не доскролить, то есть картинка не пригодится.
      А пагинация это не всегда приемлемый (по разным соображениям) костыль.


      1. noodles
        31.05.2019 22:04

        Экономия трафика это понятно.

        Под пагинацией — имел ввиду любой интерфейсный триггер, который явно делает пользователь чтоб подгрузить дополнительный dom-контент (к примеру, «посмотреть ещё...», «далее...», в продвинутых галереях-слайдерах с опцией lazy — кнопка «следующий слайд», сюда также можно отнести инфинити-скролл, и т.д.).

        По ощущениям, как раз-таки lazy-подгрузка картинок (замена атрибутов data-src на src) в зависимости от расстояния до видимого экрана выглядит «больше» костылём, чем явная команда пользователя «дайте ещё контента».

        Для себя так понял, что ленивая загрузка (именно картинок, не dom), имеет смысл, если каким-то образом дизайнер спроектировал интерфейс, в котором много «толстых» картинок занимающих два и более вьюпорта. Но по субъективному мнению, в таких случаях просится пагинация в том или ином виде.


        1. dom1n1k
          31.05.2019 22:45
          +1

          Что делать, если у меня есть статья-лонгрид с 20 картинками, размазанными по всей её длине? Не карточки в интернет-магазине, не посты в ленте — одна цельная статья.


          1. noodles
            01.06.2019 11:21
            +1

            Большое превью с кнопкой «читать далее...». Если уж заинтересовало, то вероятно статью прочитает/пролистает всю..)

            Но согласен — кейс валидный.


  1. BuccapuoH
    01.06.2019 21:51

    У нас, помню тоже была похожая ситуация в React-приложении. IntersectionObserver использовать не стали — ибо была нужна поддержка IE11. Наш выбор пал на react-waypoint. Сработал на ура, если отбросить кое-какие ограничения по стилям родителя и сузить использование до ленивого рендеринга (+ ленивой загрузки) react-компонентов.


    Для более гранулированного контроля, конечно, вряд ли подойдёт.


  1. mishunov
    03.06.2019 13:48

    Будьте добры удалить из текста перевода иллюстрацию на использование которой вам никто не давал прав.


    1. ru_vds Автор
      03.06.2019 14:04

      Спасибо за вашу бдительность, поменяли изображение.


      1. mishunov
        03.06.2019 14:47

        Благодарю за оперативность.