Рекомендации по ленивой загрузке

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

Следите за складкой

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

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

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

Кроме того, возможно, вы не захотите быть настолько строгим в отношении линии сгиба как порога для запуска отложенной загрузки. Для ваших целей может быть более идеальным установить буферную зону на некотором расстоянии ниже сгиба, чтобы изображения начали загружаться задолго до того, как пользователь прокрутит их в область просмотра. Например, API Intersection Observer позволяет указать свойство rootMargin в объекте параметров при создании нового экземпляра IntersectionObserver . Это фактически дает элементам буфер, который запускает поведение отложенной загрузки до того, как элемент окажется в области просмотра:

let lazyImageObserver = new IntersectionObserver(function(entries, observer) {
  // lazy-loading image code goes here
}, {
  rootMargin: "0px 0px 256px 0px"
});

Если значение rootMargin похоже на значения, которые вы указали бы для свойства margin CSS, это потому, что так оно и есть! В этом случае нижнее поле наблюдаемого элемента (по умолчанию область просмотра браузера, но ее можно изменить на конкретный элемент с помощью свойства root ) расширяется на 256 пикселей. Это означает, что функция обратного вызова будет выполняться, когда элемент изображения находится в пределах 256 пикселей от области просмотра, и изображение начнет загружаться до того, как пользователь фактически его увидит.

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

Смещение макета и заполнители

Отложенная загрузка мультимедиа может привести к смещению макета, если не используются заполнители. Эти изменения могут дезориентировать пользователей и вызывать дорогостоящие операции по макетированию DOM, которые потребляют системные ресурсы и способствуют зависанию. Как минимум, рассмотрите возможность использования сплошного цвета заполнителя, занимающего те же размеры, что и целевое изображение, или таких методов, как LQIP или SQIP , которые намекают на содержимое элемента мультимедиа перед его загрузкой.

Для тегов <img> src должен изначально указывать на заполнитель до тех пор, пока этот атрибут не будет обновлен конечным URL-адресом изображения. Используйте атрибут poster в элементе <video> , чтобы указать на изображение-заполнитель. Кроме того, используйте атрибуты width и height в тегах <img> и <video> . Это гарантирует, что переход от заполнителей к окончательным изображениям не изменит отображаемый размер элемента при загрузке мультимедиа.

Задержки декодирования изображения

Загрузка больших изображений в JavaScript и помещение их в DOM может связать основной поток, в результате чего пользовательский интерфейс перестанет отвечать на запросы в течение короткого периода времени, пока происходит декодирование. Асинхронное декодирование изображений с использованием метода decode перед вставкой их в DOM может сократить количество подобных хлопот, но будьте осторожны: оно пока доступно не везде и усложняет логику отложенной загрузки. Если вы хотите его использовать, вам нужно будет его проверить. Ниже показано, как вы можете использовать Image.decode() с запасным вариантом:

var newImage = new Image();
newImage.src = "my-awesome-image.jpg";

if ("decode" in newImage) {
  // Fancy decoding logic
  newImage.decode().then(function() {
    imageContainer.appendChild(newImage);
  });
} else {
  // Regular image load
  imageContainer.appendChild(newImage);
}

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

Когда что-то не загружается

Иногда медиа-ресурсы по тем или иным причинам не загружаются и возникают ошибки. Когда это может произойти? Это зависит от ситуации, но вот один гипотетический сценарий для вас: у вас есть политика кэширования HTML на короткий период времени (например, пять минут), и пользователь посещает сайт , или пользователь оставил устаревшую вкладку открытой на длительный период. времени (например, несколько часов) и возвращается, чтобы прочитать ваш контент. В какой-то момент этого процесса происходит перераспределение. Во время этого развертывания имя ресурса изображения изменяется из-за управления версиями на основе хэша или вообще удаляется. К тому моменту, когда пользователь выполняет ленивую загрузку изображения, ресурс становится недоступным и, следовательно, выходит из строя.

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

var newImage = new Image();
newImage.src = "my-awesome-image.jpg";

newImage.onerror = function(){
  // Decide what to do on error
};
newImage.onload = function(){
  // Load the image
};

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

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

Доступность JavaScript

Не следует предполагать, что JavaScript всегда доступен. Если вы собираетесь отложенно загружать изображения, рассмотрите возможность использования разметки <noscript> , которая будет отображать изображения в случае, если JavaScript недоступен. Простейший возможный резервный пример предполагает использование элементов <noscript> для показа изображений, если JavaScript отключен:

Я образ!

Если JavaScript отключен, пользователи будут видеть как изображение-заполнитель, так и изображение, содержащееся в элементах <noscript> . Чтобы обойти эту проблему, поместите класс no-js в тег <html> следующим образом:

<html class="no-js">

Затем поместите одну строку встроенного скрипта в <head> перед запросом каких-либо таблиц стилей через теги <link> , который удаляет класс no-js из элемента <html> , если JavaScript включен:

<script>document.documentElement.classList.remove("no-js");</script>

Наконец, используйте CSS, чтобы скрыть элементы с классом lazy, когда JavaScript недоступен:

.no-js .lazy {
  display: none;
}

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