Ленивая загрузка изображений средствами браузера

Автор оригинала: Addy Osmany
  • Перевод
  • Tutorial
Автор материала, перевод которого мы сегодня публикуем, Эдди Османи из Google, говорит, что уже в Chrome 75 вполне может появиться поддержка нового атрибута элементов <img> и <iframe> loading. Это означает, что данные элементы будут поддерживать стандартные возможности по так называемой «ленивой загрузке» данных.

image

Если вам не терпится узнать о том, как использование нового атрибута выглядит в коде, то взгляните на этот пример:

<img align="center" src="celebration.jpg" loading="lazy" alt="..." />
<iframe src="video-player.html" loading="lazy"></iframe>

Сейчас мы поговорим о том, как будет работать атрибут loading.

Предварительные сведения


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

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


Страница, загружающая 211 изображений. Версия страницы, при создании которой ленивая загрузка изображений не используется, сразу загружает 10 Мб графических данных. Та же страница, использующая ленивую загрузку (с помощью LazySizes) предварительно загружает лишь 250 Кб графической информации. Всё остальное подгружается по мере продвижения пользователя по странице. Подробности об этом эксперименте можно посмотреть на webpagetest.org.

Что если бы браузер мог бы помочь программисту избежать загрузки изображений, которые находятся за пределами видимой области страниц? Это благотворно сказалось бы на скорости загрузки данных в видимой области страницы, снизило бы, на маломощных устройствах, общий объём передаваемых по сети данных, уменьшило бы потребление памяти. Скоро всё это будет возможно благодаря новому атрибуту элементов <img> и <iframe> loading.

Атрибут loading


Атрибут loading позволяет браузеру откладывать загрузку содержимого элементов <img> и <iframe>, находящихся за пределами видимой области страницы, до тех пор, пока пользователь, прокручивая страницу, не окажется достаточно близко к этим элементам. Этот атрибут поддерживает три значения:

  • lazy: указывает на материалы, которые являются хорошими кандидатами на ленивую загрузку.
  • eager: материалы в элементах с таким значением атрибута нужно загрузить без промедления.
  • auto: браузер самостоятельно примет решение о том, применять ли к материалам с этим значением атрибута ленивую загрузку.

Если значение атрибута loading не указывать — это будет равносильно установке его в значение auto.


В настоящее время работа над атрибутом loading для элементов <img> и <iframe> ведётся в рамках стандарта HTML

Примеры


Атрибут loading работает с элементами <img> (в том числе с атрибутом srcset и внутри элемента <picture>), а также с элементами <iframe>.

<!-- Выполнить ленивую загрузку изображения, находящегося за пределами видимой области страницы, в тот момент, когда пользователь прокрутит страницу близко к этому изображению -->
<img align="center" src="unicorn.jpg" loading="lazy" alt=".."/>

<!-- Не выполнять ленивую загрузку, загрузить изображение немедленно -->
<img align="center" src="unicorn.jpg" loading="eager" alt=".."/>

<!-- Решение о том, применять ли ленивую загрузку при работе с изображением, примет браузер -->
<img align="center" src="unicorn.jpg" loading="auto" alt=".."/>

<!-- Выполнить ленивую загрузку изображений в <picture>. За загрузку изображений отвечает элемент <img>, в результате элемент <picture> и атрибут srcset на это не влияют -->
<picture>
  <source media="(min-width: 40em)" srcset="big.jpg 1x, big-hd.jpg 2x">
  <source srcset="small.jpg 1x, small-hd.jpg 2x">
  <img align="center" src="fallback.jpg" loading="lazy">
</picture>

<!-- Выполнить ленивую загрузку изображения, для которого задан атрибут srcset -->
<img align="center" src="small.jpg"
     srcset="large.jpg 1024w, medium.jpg 640w, small.jpg 320w"
     sizes="(min-width: 36em) 33.3vw, 100vw"
     alt="A rad wolf" loading="lazy">

<!-- Выполнить ленивую загрузку содержимого элемента <iframe> в тот момент, когда пользователь прокрутит страницу так, что её видимая область окажется поблизости от этого элемента -->
<iframe src="video-player.html" loading="lazy"></iframe>

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

Обратите внимание на то, что я предложил, чтобы атрибут, о котором идёт речь, назвали бы именно loading, так как такой подход к его именованию соответствует тому, что использовался при выборе имени для атрибута decoding. Предыдущие предложения, наподобие lazyload, не подошли, так как нужно было, чтобы этот атрибут поддерживал бы несколько значений (lazy, eager и auto).

Проверка поддержки атрибута loading браузерами


Мы принимали во внимание важность возможности применения JavaScript-библиотек для организации ленивой загрузки материалов (для кросс-браузерной поддержки этой возможности). Проверить, поддерживает ли браузер атрибут loading, можно так:

<script>
if ('loading' in HTMLImageElement.prototype) { 
    // Браузер поддерживает `loading`.
} else {
   // Иначе - загрузить и применить полифилл или JavaScript-библиотеку для 
   // организации ленивой загрузки материалов.
}
</script>

Обратите внимание на то, что атрибут loading можно использовать для прогрессивного расширения возможностей страницы. Браузеры, которые поддерживают этот атрибут, смогут организовать ленивую загрузку материалов при использовании loading=lazy, а браузеры, которые эту возможность не поддерживают, будут просто, как и прежде, загружать эти материалы.

Кросс-браузерная ленивая загрузка изображения


Если важна кросс-браузерная поддержка ленивой загрузки изображений, тогда недостаточно просто определить то, поддерживает ли браузер эту возможность, и, если это не так, воспользоваться соответствующей библиотекой, если при описании изображения в разметке используется конструкция наподобие <img align="center" src=unicorn.jpg loading=lazy />.

В разметке нужно использовать что-то наподобие <img data-src=unicorn.jpg /> (а не src, srcset или <source>) для того чтобы избежать срабатывания обычной загрузки изображения браузерами, которые не поддерживают новый атрибут. Для того чтобы менять подобные атрибуты на те, которые нужно использовать при поддержке браузером атрибута loading, или для загрузки соответствующей библиотеки в том случае, если этот атрибут не поддерживается, можно использовать возможности JavaScript.

Вот пример того, как это может выглядеть:

<!-- Загрузим это изображение, находящееся в видимой области страницы, как обычно -->
<img align="center" src="hero.jpg" alt=".."/>

<!-- Работая с остальными изображениями, применим методику ленивой загрузки -->
<img data-src="unicorn.jpg" loading="lazy" alt=".." class="lazyload"/>
<img data-src="cats.jpg" loading="lazy" alt=".." class="lazyload"/>
<img data-src="dogs.jpg" loading="lazy" alt=".." class="lazyload"/>

<script>
(async () => {
    if ('loading' in HTMLImageElement.prototype) {
        const images = document.querySelectorAll("img.lazyload");
        images.forEach(img => {
            img.src = img.dataset.src;
        });
    } else {
        // Динамически импортируем библиотеку LazySizes
        const lazySizesLib = await import('/lazysizes.min.js');
        // Инициализируем LazySizes (читаем data-src & class=lazyload)
        lazySizes.init(); // lazySizes применяется при обработке изображений, находящихся на странице.
    }
})();
</script>

Рассмотрим некоторые особенности этого кода:

  • Изображения, которые видны пользователю сразу после загрузки страницы, описаны с помощью обычных тегов <img>. Использование атрибута data-src без src приведёт к тому, что изображения, сразу после загрузки страницы, показаны не будут, поэтому, настраивая те изображения, которые должны быть видны сразу же после загрузки страницы, мы должны указывать атрибут src.
  • При описании изображений, которые, сразу после загрузки страницы, пользователю не видны, мы используем атрибут data-src. Делается это для того чтобы предотвратить их обычную загрузку в браузерах, не поддерживающих атрибут loading. Если браузер этот атрибут поддерживает, мы меняем data-src на src.
  • Если атрибут loading не поддерживается, мы загружаем вспомогательную библиотеку (lazySizes) и инициализируем её. Здесь мы используем class=lazyload для того чтобы указать библиотеке LazySizes на изображения, с которыми мы хотим работать, используя методику ленивой загрузки.

Демонстрационный пример


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

Особенности реализации поддержки атрибута loading в Chrome


Мы настоятельно рекомендуем, прежде чем пользоваться атрибутом loading в продакшне, дождаться появления его поддержки в стабильном релизе Chrome. Если же вам не терпится испытать эту возможность, тогда, возможно, вам будет интересно то, о чём мы сейчас поговорим.

Испытание атрибута loading


Для того чтобы прямо сейчас испытать новый атрибут, перейдите к странице настройки флагов Chrome (chrome://flags), включите флаги Enable lazy frame loading и Enable lazy image loading, и перезагрузите браузер.

Настройки браузера


Реализация методики ленивой загрузки материалов в Chrome основана не только на том, насколько близко видимая область страницы находится к этим материалам, но и на скорости соединения. Пороговые значения срабатывания ленивой загрузки материалов для разных скоростей соединения жёстко заданы в коде, но эти значения можно переопределить средствами командной строки. Вот пример переопределения настроек для изображений:

canary --user-data-dir="$(mktemp -d)" --enable-features=LazyImageLoading --blink-settings=lazyImageLoadingDistanceThresholdPxUnknown=5000,lazyImageLoadingDistanceThresholdPxOffline=8000,lazyImageLoadingDistanceThresholdPxSlow2G=8000,lazyImageLoadingDistanceThresholdPx2G=6000,lazyImageLoadingDistanceThresholdPx3G=4000,lazyImageLoadingDistanceThresholdPx4G=3000 'https://mathiasbynens.be/demo/img-loading-lazy'

Вышеприведённая команда соответствует текущим настройкам, применяемым по умолчанию. Для того чтобы загрузка изображений начиналась бы в том случае, если позиция прокрутки страницы находится на расстоянии в 400 пикселей от изображения, все значения в этой команде нужно поменять на 400. Ниже приведён пример 1-пиксельной вариации этой команды (такая настройка используется в вышеупомянутом видео).

canary --user-data-dir="$(mktemp -d)" --enable-features=LazyImageLoading --blink-settings=lazyImageLoadingDistanceThresholdPxUnknown=1,lazyImageLoadingDistanceThresholdPxOffline=1,lazyImageLoadingDistanceThresholdPxSlow2G=1,lazyImageLoadingDistanceThresholdPx2G=1,lazyImageLoadingDistanceThresholdPx3G=1,lazyImageLoadingDistanceThresholdPx4G=1 'https://mathiasbynens.be/demo/img-loading-lazy'

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

Инструменты разработчика


Особенности реализации поддержки атрибута loading в Chrome заключаются в том, что браузер, при загрузке страницы, запрашивает первые 2 Кб изображений у сервера в том случае, если он поддерживает технологию range request (запрос на диапазон). В первых 2 Кб изображений, наверняка, содержатся сведения об их размерах. Это позволяет браузеру генерировать местозаполнители, имеющие размеры, соответствующие размерам изображений. Кроме того, в эти 2Кб, если речь идёт о небольших изображениях вроде значков, весьма вероятно, входит всё изображение.


Загрузка фрагментов графических файлов

Chrome загружает оставшиеся данные изображений в тот момент, когда пользователь близок к тому, чтобы их увидеть. При работе с инструментами разработчика это может привести к тому, что в панели Network может «появиться» информация о двух загрузках изображения, а в панели Resource Timing будут выведены сведения о двух запросах, выполняемых для загрузки каждого изображения.

Определение сервером поддержки браузером атрибута loading


Если бы мы жили в совершенном мире, то для того чтобы узнать о том, нужно ли, для правильного вывода страницы в некоем браузере, использовать вспомогательную библиотеку, можно было бы не полагаться на анализ браузера средствами клиентского JavaScript. При таком подходе сервер, заранее зная, нужна такая библиотека или нет, включил бы (или не включил бы) её в состав страницы, отправляемой браузеру. Подобную проверку может сделать возможной использование технологии HTTP Client Hint, благодаря которой клиент способен передавать серверу «подсказки» о своих возможностях.

Соответствующая «подсказка», касающаяся поддержки атрибута loading, находится сейчас на ранней стадии рассмотрения.

Итоги


Автор этого материала предлагает всем, кто заинтересован в использовании атрибута loading при работе с элементами <img> и <iframe>, опробовать его, и поделиться с ним впечатлениями. Особенно ему интересно узнать о том, как, с точки зрения разработчиков, выглядят предложенные здесь механизмы кросс-браузерной поддержки ленивой загрузки данных, и о том, не упустил ли он, рассказывая о таких механизмах, какие-то пограничные случаи.

Уважаемые читатели! Планируете ли вы использовать атрибут loading в своих проектах?

RUVDS.com
933,51
RUVDS – хостинг VDS/VPS серверов
Поделиться публикацией

Комментарии 13

    +2
    Ещё бы эту штуку в css.
      –1
      Все изображения в СSS добавленные через background и так загружаются после рендера документа. То есть они лейзи лоадинг по умолчанию.
        +4

        Lazyload это загрузка изображений перед тем как они попадут в область видимости а не после рендера документа.


        Для background можно имитировать lazyload при помощи .lazy{background-image: none !important} либо .lazy{display: none !important}. Но при этом нужен скрипт который будет отслеживать координаты элемента и снимать с него класс lazy в нужный момент.

          0
          Да, все именно так. И в то же время все скрытые табы, выпадающие меню, попапы будут грузить картинки только по необходимости. Да и рендер документа без задержек дает отличный прирост производительности. Слежу за loading аттрибутом и его поддержкой. Будет полезная фича. Пока есть IntersectionObserver — если на него повесить присвоение и удаление класа из вашего примера не нужно будет следить за координатами.
      +8
      Настораживает то что умолчательное значение после внедрения стандарта совсем не то которое было до внедрения стандарта.
        0
        Здорово, спустя тысячу лет, веб-разработка избавится от ещё одного костыля. Ну, а пока придётся его немного усложнить, чтобы поддерживать частичную поддержку.
          +1
          О, отлично. Наконец-то можно будет сделать простой и универсальный способ отключить эту бесячую хрень раз и навсегда на всех сайтах.
            0
            Как же я ненавижу ленивую загрузку изображений на сайтах: вместо того, чтобы сразу видеть все изображения и быстро перемещаться по статье, приходится ждать когда же загрузятся очередные изображения до которых ты доскроллил.
              0

              А что, так долго грузятся? Если их не сжимать нормально, то тут уже что с lazy, что без будет беда.

                0
                Сжимать, использовать правильное разрешение, кеширование, http2, не надеяться, что lazy спасет от медленной сети.
                  0
                  Даже если недолго — это критично. Даже секунда напрягает. Например, я открываю в новой вкладке статью с medium. Я хочу, чтобы, когда я открываю статью, она была уже полностью загружена и я мог еще прочитать так как я хочу: долго и вдумчиво, просто проскролить, быстро пройтись по статье через сколлбар или как-то по другому. При этом время первоначальной загрузки статьи мне не критично: когда загрузится, тогда и начну читать. А с этой ленивой загрузкой, например, когда я скролю статью, приходиться делать постоянные остановки, когда ждешь, когда прогрузятся очередная порция картинок.
                0
                Сомнительно что другие браузеры тоже оперативно включаться в поддержку данного атрибута. Если с img и picture ещё плюс минус понятно, то как вот подключение данного атрибута в iframe они себе представляют реализовать — нет, чёт они очень жизнь себе усложнили, начали б с малого
                  +1
                  ИМХО, поведение загрузки должен устанавливать пользователь браузера.

                  За МКАД-ом, знаете ли ещё есть локации, не избалованные доступным везде как воздух интернетом. Если скорость интернета хреновая или есть планы почитать страничку в удалении от вайфая, то проще открыть страницу и пока грузится ВСЯ статья с картинками «пойти попить чаю». А потом нормально её почитать.

                  И если оно LAZY — то меня ждёт облом. Так же, как и с ненавистным «бесконечным скроллом». Придётся для статьи с lazy-картинками точно так же предварительно специально её проскролливать.

                  Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                  Самое читаемое