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");
    });
  });
})

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

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

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

Complete Intro to React v2 (Ангельский)
Этот видеокурс гораздо больше чем интро. Брайан проведет вас через реальный мир веб-приложений, что даст вам полную, боевую картинку окружения React. Брайан работал на таких проектах как Netflix и Reddit, так что в его компетентности можете не сомневатся. Используйте JSX, стройте компоненты React, а также в этом курсе: Babel, ESLint, Yarn, Jest и Webpack 2. Ну и на последок познакомитесь с последним React router v4 плюс Redux.
Complete Intro to React v2 (Ангельский)
9 уроков
Скилл: Advanced
Node.js on Amazon Web Services (Ангельский)
Посмотри этот видеокурс, который научит тебя создания, тестирования, развертывания и масштабирования веб-приложений Node.js на Amazon Web Services.
Node.js on Amazon Web Services (Ангельский)
58 уроков
Скилл: Годлайк
ES6: The Right Parts (Ангельский)
Чувствуйте себя комфортно с последней эволюций великого языка JavaScript. При кодировании вместе с Кайлом в этом курсе вы научитесь: использовать 'Let' и 'Const', литералы, итераторы и генераторы. Плюс научитесь писать стрелочные функции, дефолтные параметры, и другие свойста и методы. Учитесь использовать эти новые возможности ES6, чтобы писать чище и более продуктивнее.
ES6: The Right Parts (Ангельский)
46 уроков
Скилл: Правильный
Метод Scrum для веб-разработки
Scrum позволяет работать со своей коммандой результативно, полностью контролируя все этапы разработки (при грамотном подходе и в руках нужного человека). Узнай в этом кратком видеокурсе об этом подходе, и совершенствуй свои результаты, закрывая проекты без просроченных дедлайнов.
Метод Scrum для веб-разработки
13 уроков
Скилл: Successful
Game Development with Unity 5
Разработка игр постоянно динамично меняется и совершенствуется. Эта область постоянно в движении, и если вы не будете осторожны, вы можете остаться позади. Вот почему так важно держать свои навыки на высшем уровне и ознакомиться с новейшими инструментами и программами. Этот курс научит вас работать с Unity5, новейшей версии одного из ведущих в отрасли движка.
Game Development with Unity 5
113 уроков
Скилл: EA
Pro Tools 12 Essential Training
Pro Tools является отраслевым стандартом программного обеспечения для музыки и пост-продакшн. Этот курс охватывает основные концепции и методы, необходимые для записи, редактирования, микширования и мастеринга в Pro Tools. Композитор / продюсер Skye Lewin учит, как создавать музыку с виртуальными инструментами и плагинами, работать с аудио и видео файлами, и делать как основные изменения, так и более сложные операции с инструментами, как Elastic Time и Pitch. Являетесь ли вы продюсером, звукорежиссёром, или радиолюбителем - этот курс может помочь вам стать специалистом в Pro Tools 12.
Pro Tools 12 Essential Training
116 уроков
Скилл: Dr.Dre