Javascript-форум (https://javascript.ru/forum/)
-   Events/DOM/Window (https://javascript.ru/forum/events/)
-   -   Ленивая загрузка изображений через Promise. Цепочка. (https://javascript.ru/forum/events/76762-lenivaya-zagruzka-izobrazhenijj-cherez-promise-cepochka.html)

Duda.Ml1986@gmail.com 11.02.2019 14:42

Ленивая загрузка изображений через Promise. Цепочка.
 
Здравствуйте,
Нужно было сделать отложенную загрузку изображений.
Для этого перед выводом html все src для img заменил на data-src. (php)
Что бы не грузились изначально.
Потом через Promise создавал изображения с путями из спрятанных файлов, параметр брался из data-src. Когда созданное изображение загружалось, я добавлял отсутствующий атрибут src скрытому изображению.

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




images_sidebar = $('#sidebar_menu_news_container img[data-src]');
    $.each( images_sidebar, function( key, value ) {
        lazeImageLoad(value);
    });

    function lazeImageLoad(image) {
        let promise = new Promise((resolve, reject) => {
            img = document.createElement('img');
            img.src = $(image).attr('data-src');

            if (img.complete) {
                resolve(image);
            } else {
                img.addEventListener('load',  resolve(image));
            }
        });

        promise
            .then(
                result => {
                image.src = $(image).attr('data-src');
        },
            error => {}
        );
    }

Nexus 11.02.2019 14:54

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

Решение вашей проблемы без обещаний (недостает обработки ошибок):
const queue=document.querySelectorAll('img[data-src]');
(function loadImage(){
    if(!queue.length)
        return;

    const node=queue.shift();
    node.onload=loadImage;
    node.src=this.dataset.src;
})();

Malleys 11.02.2019 21:22

Цитата:

Сообщение от Duda.Ml1986@gmail.com
все src для img заменил на data-src.

Теперь это у вас не картины (точней элемент находится в таком состояний, что он не представляет картинку) и если, что-то пойдёт не так, то совсем их не будет видно. Совсем!

Цитата:

Сообщение от Nexus
Вам стоит добавить изображениям src указывающий на изображение-заглушку.

src предназначен для того, чтобы указать путь к полной картинке.

Вопрос в том, чтобы эта картинка показывалась только тогда, когда окажется на экране пользователя. Но как отличить картинку, которая загрузилась, от картинки, которая ещё не загрузилась? Ведь вы можете захотеть показывать вместо неё заглушку!

Картинка может быть определена так <img src="pic.jpg">, но можно ведь, например, в search params указать, что это картинка для ленивой загрузки! <img src="pic.jpg?lazy"> И тогда можно сказать, что pic.jpg?lazy это заглушка, а удалив lazy мы получим настоящую pic.jpg, когда она попадёт в область видимости!

Вот пример, реализованный при помощи отслеживания запросов, если в search params есть lazy, то можно даже не делать запрос на сервер, ведь заглушка может быть выполнена при помощи CSS или SVG. Попадёт ли в область видимости узнаётся при помощи Intersection Observer API. Пример... https://plnkr.co/edit/PxM3Poomc4JfXT5BlnyN?p=preview

Nexus 11.02.2019 22:38

Malleys, я правильно понял, что вы предлагаете добавить логику, по которой сервер будет отдавать пользователю статику?
В противном случае этот lazy-load, как собаке пятая лапа.

upd. да и с логикой такая же ненужная хрень. С такой ленивой загрузкой на сервер будет отправляться в 2 раза больше запросов.
Я что-то упустил или вы действительно описали не очень хороший метод реализации?

laimas 11.02.2019 23:17

Malleys,
Nexus,

вот как ребята выкручиваются. )

Malleys 11.02.2019 23:44

Цитата:

Сообщение от Nexus
я правильно понял, что вы предлагаете добавить логику, по которой сервер будет отдавать пользователю статику?

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

Если на странице есть <img src="pic.jpg"> и <img src="pic.jpg?lazy">, то браузер сразу начнёт скачивать картинки, но мы можем отслеживать событие fetch, и следовательно решать, нужно ли, что-то скачивать с сервера или нет. Если в пути содержится параметр lazy, то мы можем сами составить ответ на этот запрос. Например, это может быть пустая SVG картинка.
const image = new Response(`
	<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1 1">
	</svg>
`, {
	headers: { "Content-Type": "image/svg+xml" }
});

addEventListener("fetch", function(event) {
	const { searchParams } = new URL(event.request.url);

	if(searchParams.has("lazy")) {
		event.respondWith(image.clone());
	}
});
Этот код в отдельном файле(mediator.js), подключается к приложению так...
<script>
	navigator.serviceWorker.register("mediator.js").then(console.log, console.error);
</script>
Это так, пример, а так там может быть своя логика, что делать при успешной регистрации или отторжении.

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

Цитата:

Сообщение от Nexus
Я что-то упустил или вы действительно описали не очень хороший метод реализации?

Да, может быть вы не заметили Service Worker. Вообще это круто, да? Дописываешь lazy и оно не загружается, а потом убираешь из адреса lazy и оно загружается. И вёрстка простая <img src="pic.jpg?lazy">, а не <img class="lazy-loading-image" data-src="pic.jpg"> и пр. обёртки с <template>. Вообще, конечно, можно сделать так, чтобы параметр lazy не приписывать. Вообще супер! Конечно при помощи Service Worker можно сделать более интересные вещи!

рони 12.02.2019 00:34

Malleys,
то есть в примере изначально картинок нет, и они погружаются потом по мере скролла?

Nexus 12.02.2019 08:17

Malleys,
Цитата:

Сообщение от Malleys
Вообще это круто, да?

Да, это круто)
Первый раз встречаю такой способ реализации.
Спасибо, что поделились.

рони 12.02.2019 10:46

Цитата:

Сообщение от Nexus
Спасибо, что поделились.

присоединяюсь, только не могу увидеть результат, возможно не туда смотрю или что-то не знаю или не понимаю.(Firefox, Google Chrome)

Nexus 12.02.2019 11:09

Цитата:

Сообщение от рони
то есть в примере изначально картинок нет, и они погружаются потом по мере скролла?

Изначально картинки есть, но service worker подменяет их заглушками (если в адресе изображения есть параметр "lazy"), т.е. они (изображения) фактически не загружаются.
Оригинальные изображения (вместо заглушек) загружаются, когда observer удаляет из их адреса параметр "lazy", т.к. в адресе изображения названный параметр отсутствует, то worker больше не препятствует запросу к серверу.

Здесь видно момент, когда заглушка заменяется оригинальным изображением:
https://plnkr.co/edit/ed1gNMgZWTA4IT5bwZ41?p=preview


Часовой пояс GMT +3, время: 06:58.