6 Января 2017

Lazy Loading ( ленивая загрузка ) изображений на сайте

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

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

Проблема веса страницы

Эта проблема не является уникальной для этого сайта. По словам HTTP Archive, изображения в настоящее время занимают 63% веса всех страниц. Как разработчики, мы упорно работаем, чтобы оптимизировать и минифицировать наш код JavaScript и CSS ( но не на этом проекте ), чтобы сделать их как можно меньше, но по сути именно снижения веса изображений сможет кардинально ускорить ваши страницы.

По состоянию на май 2015, средний вес веб-страницы превысил отметку 2 МБ. Это почти в два раза превышает размер средней страницы всего три года назад.

Так что же мы можем сделать?

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

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

Пример страницы

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

Решение

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

  • Построить HTML так, чтоб не загружать автоматически изображения (обычно это делается путем указания атрибута в теге img);
  • Следить за изменением viewport во время скроллинга пользователя на сайте, для того чтобы увидеть какие изображения входят в область просмотра;
  • Поменять атрибут, для того чтобы изображения показалось.

В первом шаге вы можете быть обеспокоены тем, что IMG тег без SRC не является допустимым в HTML. К сожалению вы не можете поместить фактический источник в атрибуте SRC? нам нужно как-то предотвратить загрузку изображения с помощью JavaScript. Вы можете поместить "манекен" исходного изображения, например легкое пустое полотно или прелоадер.

Давайте начнем с написания простого JavaScript…

Простой JAVASCRIPT

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

img data-src="images/Robin.jpg" alt=""

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

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


function isElementInViewport (el) {

    var rect = el.getBoundingClientRect();

    return (
        rect.top >= 0 &&
        rect.left >= 0 &&
        rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) && /*or $(window).height() */
        rect.right <= (window.innerWidth || document.documentElement.clientWidth) /*or $(window).width() */
    );
}

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

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


//these handlers will be removed once the images have loaded
window.addEventListener("DOMContentLoaded", lazyLoadImages);
window.addEventListener("load", lazyLoadImages);
window.addEventListener("resize", lazyLoadImages);
window.addEventListener("scroll", lazyLoadImages);

function lazyLoadImages() {
  var images = document.querySelectorAll("#main-wrapper img[data-src]"),
      item;
 // load images that have entered the viewport
  [].forEach.call(images, function (item) {
    if (isElementInViewport(item)) {
      item.setAttribute("src",item.getAttribute("data-src"));
      item.removeAttribute("data-src")
    }
  })
 // if all the images are loaded, stop calling the handler
  if (images.length == 0) {
    window.removeEventListener("DOMContentLoaded", lazyLoadImages);
    window.removeEventListener("load", lazyLoadImages);
    window.removeEventListener("resize", lazyLoadImages);
    window.removeEventListener("scroll", lazyLoadImages);
  }
}

Первое что мы делаем, это убеждаемся что мы наблюдаем за изменений в области просмотра слушая DOMContentLoaded, load, resize и scroll события. Каждый раз, когда одно из этих событий происходит мы вызываем метод, который проверит есть ли какие-либо изображения которые вошли в область просмотра.

Глядя на метод lazyLoadImages, мы сначала получаем все изображения, которые еще не загружены. Мы делаем это, выбирая только те которые до сих пор имеют data-src. (Как мы увидим в последующих примерах, существует целый ряд способов сделать это, но, если честно, я не проверял, какой метод является более производительным.)

Если изображение вошло в viewport, мы переставляем значение data-src в сам SRC и удаляем data-src. И, наконец, если изображений, которые еще не загрузились нет, мы просто удаляем слушатель событий.

JQUERY

Если вы используете JQuery на вашем сайте, вы можете сэкономить несколько строк кода.


$(window).on('DOMContentLoaded load resize scroll', function () {;
  var images = $("#main-wrapper img[data-src]");
 // load images that have entered the viewport
  $(images).each(function (index) {
    if (isElementInViewport(this)) {
      $(this).attr("src",$(this).attr("data-src"));
            $(this).removeAttr("data-src");
    }
  })
 // if all the images are loaded, stop calling the handler
  if (images.length == 0) {
    $(window).off('DOMContentLoaded load resize scroll')
  }
})

// source: http://stackoverflow.com/questions/123999/how-to-tell-if-a-dom-element-is-visible-in-the-current-viewport/7557433#7557433
function isElementInViewport (el) {
    var rect = el.getBoundingClientRect();

    return (
        rect.top >= 0 &&
        rect.left >= 0 &&
        rect.bottom <= $(window).height() &&
        rect.right <= $(window).width()
    );
}

Проблемы, которые создают такие рещения

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

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

Библиотеки

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

LAZYLOAD

Одна из первых библиотек ленивой загрузки изображений.

Одно из различий между другими решениями является то, что изображения используют атрибут data-original а не data-src.

img data-original="images/GreenLantern.jpg" alt="" width="374" height="260"

После того, как наши образы создаются, сделать ленивую загрузку очень просто. Просто включите файл JavaScript (очевидно да) и инициализируйте LazyLoad.


var myLazyLoad = new LazyLoad({
  threshold: 50,
  callback_load: function(e) {
    console.log($(e).attr("data-original") + " loaded" );
  }
});

Изображение ниже показывает прокрутку через мобильную версию страницы.

BLAZY.JS

BLAZY.JS является сравнительно новой библиотекой, которая призвана предложить ряд ключевых функций, оставаясь при этом небольшой и легкой. Одно существенное отличие Blazy является то, что она не требует JQuery.

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

img data-src="images/Batgirl.jpg" alt="" width="374" height="260" class="b-lazy"

После этого вам нужно просто инициализировать сценарий.


var bLazy = new Blazy({
    offset: 50,
    success: function(e){
        console.log($(e).attr("src") + " loaded" );
  },
    error: function(ele, msg){
        console.log(msg)
  }
});

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

UNVEIL

UNVEIL еще одна библиотека, которая требует Jquery.

Одна хорошая вещь в Unveil - она не требует каких-либо специальных разметок на изображениях за пределами атрибута data-src.

img data-src="images/WonderTwins.jpg" alt=""

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


$(function () {
  $("#main-wrapper img").unveil(50, function() {
    $(this).load(function() {
      console.log(this.src + " loaded");
    });
  });
})

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

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

Последние курсы на сайте:

Веб-разработчик 5.0
Струна, пистолеты и снайперскай винтовка - все это в прошлом для лысого из Hitman. Сорок седьмой взял перерыв и решил занятся веб-разработкой, где зарабатывать деревяные легче, и не надо пачкать руки. В этом видеокурсе сорок седьмой тихо научит вас всего того, что нужно знать современному веб-разработчику, и вам для этого не нужно будет проходить миссиии по несколько раз. Так как этот курс сворован, мы б на вашем месте, время от времени, присматривали б что творится у вас за спиной, так как 47 может разозлится и вернуться назад к основной работе.
Веб-разработчик 5.0
30 уроков
Скилл: Codename 47
SEO-специалист
Правильный SEO сможет привлечь пользователей и потенциальных клиентов на ваш ресурс практические бесплатно. В этом видеокурсе ты узнаешь как правильно организовать свой сайт для того, чтобы посиковые системы с легкостью понимали ваш контент и поднимали его в выдаче.
SEO-специалист
13 уроков
Скилл: Это SEO, детка
Secure for Web Apps & APIs Using JWTs
Нетривиальным веб-приложениям требуется некоторая форма аутентификации пользователя. Когда дело доходит до реализации аутентификации в современных одностраничных приложениях, все может стать немного сложнее, и традиционные методы сессии и cookie-based авторизация, как правило, мешают. Спецификация JSON Web Token предлагает гораздо лучший способ обработки аутентификации в SPA и имеет множество преимуществ.
Secure for Web Apps & APIs Using JWTs
29 уроков
Скилл: Advanced
ES6 for everyone
Цель этого курса проста: значительно укрепить основные навыки JavaScript и подготовить вас до написания современного JavaScript.
ES6 for everyone
67 уроков
Скилл: Middle
Building Awesome Web Apps with Angular 2
Получите общий обзор основных частей Angular 2 и узнайте как они сочетаются друг с другом, чтобы создать мощную инфраструктуру для постройки приложений. Изучите различия между Angular 1 и Angular 2. Используйте Angular CLI, чтобы развернуть все это супер быстро, и компонируйте большие приложения Angular 2 используя сервисы, роуты и сабкомпоненты.
Building Awesome Web Apps with Angular 2
54 урока
Скилл: Advanced
ASP.NET MVC 5
Узнай как строить веб-приложения с помощью платформы ASP.NET MVC 5 на языке C#. ASP.NET MVC 5 - один из популярных фреймворков для создания веб-приложений, который реализует шаблон Model-view-controller.
ASP.NET MVC 5
64 урока
Скилл: Middle