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

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

Проблема

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

В приведенном ниже примере я создал простой веб-сайт, содержащий фоновое изображение размером 4,8 МБ. Как видите, DOM загружается за 1,14 секунды. Таким образом, пользователь видит контент через 1,14 секунды. Довольно неплохо для сети 3G. Однако загрузка фонового изображения занимает 27,32 секунды там, где пользователь видит части загружаемых изображений. Пользователь, возможно, уже покинул ваш веб-сайт в это время.

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

Решение

Так как же нам решить эту проблему? Что ж, первое, что мы можем сделать, это сжать фоновое изображение с помощью различных инструментов в Интернете. Это легкий выигрыш, который сократит время загрузки примерно до десяти секунд. Это кажется огромным шагом, но десять секунд - это слишком много.

Следующим шагом будет загрузка так называемого изображения «заполнителя» перед фактической загрузкой исходного изображения. Этот «заполнитель» представляет собой вариант исходного изображения с низким разрешением. При создании этого изображения мы уменьшили разрешение изображения с 7372x4392 пикселей до 20x11 пикселей. В результате размер изображения составляет от 4,8 МБ до 900 байт.

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

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

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

(() => {
  '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})`;
    };
  });
})();

Функция javascript сканирует DOM на предмет наличия любого класса asyncImage. После этого он загрузит все изображения, указанные в атрибуте data-src для этих элементов. После загрузки изображение заменит либо источник тега изображения, либо фоновое изображение не элемента IMG.

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

or

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

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

Заключение

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

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

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

См. Рабочий пример здесь

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

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

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

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

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