Оптимизация загрузки изображений

Привет, Хабр! Представляю вашему вниманию перевод статьи «How to optimize image loading on your website».

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

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

Проблема


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

В примере ниже я создал простой сайт с фоновым изображением, которое весит 4.8Мб. Как вы можете видеть, DOM загрузился за 1.14 секунды, т.е. фактически пользователь видит содержимое через 1.14 секунды, что довольно неплохо для 3G интернета. Однако, фоновое изображение загружается за 27.32 секунды. За это время пользователь может уже уйти с вашего сайта.



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

Решение


Так, как же решить эту проблему? Ну, для начала мы можем просто сжать наше фоновое изображение, используя различные инструменты. Например, TinyPNG, ImageOptimizer или JPEGmini. Это будет легкой победой и уменьшит время загрузки до ~10 секунд. И хотя это выглядит, как огромный шаг в решении, но 10 секунд все еще слишком много.

Следующим шагом может стать загрузка изображения-заглушки перед загрузкой изначального изображения. Заглушка — это версия оригинального изображения, но с низким разрешением. Когда мы создаем такое изображение, то уменьшаем разрешение с 7372x4392 пикселей до 20x11. В итоге размер изображения уменьшается с 4.8Мб до 900 байтов.



Это позволит уменьшить скорость загрузки с 10 секунд до 550 миллисекунд. Но теперь в качестве фона у нас заблюренное изображение с низким разрешением. Это отличное решение, пока страница загружается первые секунды, но все таки мы хотим показать пользователям оригинальное изображение с высоким разрешением.

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

(() => {
  'use strict';
  // Page is loaded
  const objects = document.getElementsByClassName('asyncImage');
  Array.from(objects).map((item) => {
    // Start loading image
    const img = new Image();
    img.src = item.dataset.src;
    // Once image is loaded replace the src of the HTML element
    img.onload = () => {
      item.classList.remove('asyncImage');
      return item.nodeName === 'IMG' ? 
        item.src = item.dataset.src :        
        item.style.backgroundImage = `url(${item.dataset.src})`;
    };
  });
})();


Этот код ищет элементы с классом asyncImage, после чего загружает все изображения, указанные в атрибуте data-src, а как только изображение загрузилось, заменит src для элемента <img>, а для остальных элементов установит фоновое изображения через css.

<div class="asyncImage" data-src="/images/background.jpg">
...
</div>

или

<img class="asyncImage" src="/images/background-min.jpg" data-src="/images/background.jpg" alt="Beautiful landscape sunrise">


Поскольку скрипт удаляет класс у элемента после загрузки изображения, то при желании мы можем сделать красивую анимацию с помощью CSS. Например, ease-in-out анимация позволит сделать исчезновение заглушки, когда заменяется изображение.

Заключение


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



Как вы можете видеть мы загружаем заглушку за 570 миллисекунд, как только она загрузится пользователь будет видеть заблюренную версию с низким разрешением нашего оригинального изображения, а как только загрузится оригинальное изображение, оно заменит версию с низким разрешением.

Посмотреть рабочий пример можно здесь

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


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

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

Ее преимущество в том, что во время первоначальной загрузки будет загружено меньше данных, так как очень часто лишь малая часть изображений отображается в видимой области страницы, а остальные можно загружать по мере того, как пользователь скроллит. Для реализации этого лучше всего подходит Intersection Observer.
AdBlock похитил этот баннер, но баннеры не зубы — отрастут

Подробнее
Реклама

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

    +2
    В посте картинка 7372x4392, в примере уже 3 686px × 2 196px
    Откуда такие числа? Не лучше ли сделать нормальную картинку для фона и забыть про эти ниндзя-техники?
      0

      Я так понимаю это сделано в угоду ретине. Но если для мелких иконок это норм, то для больших джепегов это уже перебор.

        +4
        Ну как же?!
        Все юзвери смотрят сайт на теликах 50" 4к и будут агриться на любое разрешение фона меньше, чем дефолтное у экрана потому, что смотрят его с расстояния меньше полуметра и видно КВАДРАТИКИ.
        И делают всё это они это через мобильный интернет.

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

        Кстати
        В примере по ссылке вот такие параметры изображения:
        Image properties
        Image type: JPEG (image/jpeg)
        Dimensions: 3686 × 2196 px
        Screen Size: 728 × 434 px
        File size: 892.1 kB (892,138 bytes)
        Address: i.imgur.com/EWx6Ao5.jpg
        Т.е. вместо 5МБ уже 1МБ.


        Проще сделать картинку размера FullHD со сжатием JPG порядка 10%+. И этого будет достаточно для оптимизации, кмк. Это в случае, если без бэкграунда совсем никак.
          +1
          Все юзвери смотрят сайт на теликах 50" 4к

          Ну да, как же я мог о них забыть)
          Если серьёзно, то для таких маньяков srcset изобрели.
          0
          Блин, ну это просто демо-пример, для большего контраста. В реальности картинки будут меньше, но зато не одна, а несколько.

          Вот за что действительно автору нужно попенять, так это за слишком современный js-код со стрелочными функциями, шаблонными строками и прочими финтифлюшками. Потому что: подмена картинки на LQIP подразумевает временную поломку контента. Всё что связано с поломкой контента, должно быть железобетонно надежно и работать во всех сколько-нибудь (полу)живых браузерах. Тут не свалишь на graceful degradation или progressive enhancement.
            0
            Берём ситуацию, в которой оптимизация не особо нужна, либо нужна в другом месте. Тратим время и ресурсы на написание/внедрение скриптов, которые валят часть браузеров. Получаем утяжеление сайта для всех и ухудшение некоторых метрик) Ну да, всё хорошо.
          +2
          В этом году появилась автоматическая «ленивая загрузка». Достаточно всего лишь добавить атрибут loading="lazy"

          <img src="image.png" loading="lazy">

          Картинки будут загружаться автоматически когда они попадут в область видимости. И не нужно будет вручную создавать Intersection Observer
            0
            Штука вроде и хорошая, но:
            а) Нет никакой гибкости управления (это бывает нужно, а бывает и не нужно — когда как).
            б) Поддерживается только в Хроме. IE и FF — черт с ними. Но мы ведь понимаем, что эти техники в первую очередь предназначены для мобильного рынка? Важнейший сегмент яблочных устройств оказывается за бортом.
            0
            Я бы в качестве селектора для поиска атрибута использовал сам атрибут, возможно с другим названием, но так вроде понятнее и не надо лишний класс прописывать:
            const objects = document.querySelector('[data-src]');
              0

              При таком подходе поисковики будут индексировать фото низкого разрешения. Как сделать, чтобы индексировалось фото в большом?

                0
                Может, я что-то не понимаю, но какая разница, какая картинка будет проиндексирована? title\alt же правильные, поисковики максимум показывают небольшую превьюшку, где низкого разрешения более чем достаточно
                  0

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

                    0

                    Поисковики показывают нормальную картинку и более того позволяют искать с учётом её размеров и по содержимому

                  0
                  А если микро-картинку заинлайнить в html, то ещё быстрее первичная загрузка пройдёт.
                  И в png такая малышка вообще 600 байт у меня заняла (я эту же картинку уменьшил и сохранил).
                    +1
                    Еще в комментариях не озвучили способ положить рядом картинку в webp и через вебсервер определять если браузер поддерживает webp то отсылать ему webp версию. Экокномия может быть очень существенной
                      0
                      А элемент picture из html5 не подходит?
                        0

                        Уже давно придумана такая штука, как interlaced image. В большом изображении зашита его мини версия, которую браузер первым делом подгружает и показывает. Вы могли встречать это при сохранении png в photoshop.

                          0
                          LOL, это стёб такой? background фотографией размером в 5мб? Да, сейчас не 2000 год и это изображение не качается несколько ночей с BBS. Зачем ?!!! Если мне действительно нужно это изображение, я на него нажму. Оно никому не нужно просто так. Особенно где-либо с GPRS. Делаем preview {320,640,1024}. И всё решение занимает 2 строчки.

                          ffmpeg -i image.png -s 320x240 -c:a copy image_320x240.png
                          leanify image320x240.png

                          Оптимизаторы [цензура]…

                          Так же чуть выше писали про формат webp. Так вот, я потратил некоторое время на оптимизацию изображений в lossless и единственный вывод для себя: Leanify — это круто. Пользуйтесь Leanify. Leanify выше крыши для 99% кейсов с изображениями. webp почти всегда выдаёт изображение с большим весом.

                          Всё! Простое и надёжное решение в 2 строчки. Можно ещё третьей строчкой watermark добавить и больше нет никаких проблем с оптимизацией изображений. Больше уже либо Google, либо совсем жёсткий перфекционизм.
                            0
                            Если WebP весит больше «почти всегда», то похоже с процессом конвертации что-то не так. В нормальных условиях такое бывает иногда, но обычно WebP весит всё-таки меньше, чем PNG.
                              0
                              PNG — специфичный формат. Почти всё весит меньше, чем PNG, если с его помощью сжато фотореалистичное изображение. PNG отлично подходит только для сжатия рисунков с небольшим количеством цветов и мелких деталей, идеален для сжатия GUI без потерь.
                                0
                                Кэп?
                                Разумеется, в моем комментарии речь о подходящих для PNG изображениях и lossless WebP. Иные случаи обсуждать бессмысленно.
                                Так вот даже при этих условиях WebP выигрывает и весьма ощутимо.
                                  0
                                  Речь в комментариях выше шла о сжатии фотографий, для чего PNG не подходит никак.

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

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