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

Эта проблема

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

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

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

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

Решение

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

Код

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

Начнем с компонента React. Когда я создаю новый компонент, мне нравится думать о том, что отрисовывается в первую очередь. Нам нужно отрендерить полноразмерное изображение, а затем либо сжатую версию того же изображения, либо какой-либо другой тип загрузчика. Мы объявляем preload, который возьмет источник изображения и заставит его рендерить с низким качеством. он будет помещен перед полноразмерным изображением. Контейнер div будет полезен для некоторого CSS, который мы немного представим, но он также будет полезен для передачи свойств размера, которые вы можете добавить в класс div.

import React from 'react';
class ImageLoader extends React.Component {
  render() {
    const preload = `${this.props.src}?q=20`;
    return (
      <div className="container">
        <img 
          src={preload} 
          className="placeholder" 
        />
        <img
          src={this.props.src}
          className="image"
          alt={this.props.altText}
        />
      </div>
    );
  }
}
export default ImageLoader;

Теперь поработаем над логикой. Мы хотим, чтобы изображение preload отображалось во время загрузки полноразмерного изображения. Как только полноразмерное изображение загрузится, мы хотим скрыть preload изображение. Этого можно добиться с помощью следующего метода:

updateElements() {
  const container = document.querySelector(
    `#container-${this.state.uniqueId}`
  );
  const placeholder = document.querySelector(
    `#placeholder-${this.state.uniqueId}`
  );
  const image =
    document.querySelector(`#image-${this.state.uniqueId}`);
  image.onload = () => {
    image.setAttribute("style", "opacity: 1");
    container.setAttribute("style", "background: none");
    placeholder.setAttribute("style", "display: none");
  };
}

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

render() {
  const preload = `${this.props.src}?q=20`;
  const container = `container-${this.state.uniqueId}`;
  const placeholder = `placeholder-${this.state.uniqueId}`;
  const image = `image-${this.state.uniqueId}`;
  return (
    <div id={container} className="container">
      <img 
        id={placeholder}
        src={preload}
        className="placeholder"
      />
      <img
        id={image}
        src={this.props.src}
        className="image"
        alt={this.props.altText}
      />
    </div>
  );
}

А что касается создания этих уникальных идентификаторов, нам понадобится другой метод, который будет вызываться в componentDidMount:

generateUnqiueId() {
  let uniqueId = "";
  const selection =
    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
  for (var i = 0; i < 5; i++) {
    uniqueId += selection.charAt(
      Math.floor(Math.random() * selection.length)
    );
  }
  this.setState({ 
    uniqueId: uniqueId 
  }, () =>this.updateElements());
}

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

.placeholder {
  filter: blur(50px);
  position: absolute;
  /* this is needed so Safari keeps sharp edges */
  transform: scale(1);
  transition: display 1s linear;
  width: 100%;
}

Аналогично для элемента image, мы начинаем с того, что он невидим, с непрозрачностью на 0 и анимацией перехода, когда непрозрачность изменяется.

.image {
  opacity: 0;
  top: 0;
  width: 100%;
  transition: opacity 1s linear;
  z-index: 1;
}

Элемент container - мой запасной вариант. Если preload изображение не загружается по какой-либо причине, у меня есть поле со светло-серым фоном в том месте, где будет image во время загрузки.

.container {
  background-color: #f6f6f6;
  background-size: cover;
  background-repeat: no-repeat;
  height: 100%;
  padding: 0 !important;
  position: relative;
  overflow: hidden;
  transition: background 1s linear;
  width: 100%;
}

Готовый продукт

Полный код компонента React

import React from "react";
class ImageLoader extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      uniqueId: null
    };
  }
  generateUnqiueId() {
    let uniqueId = "";
    const selection =
      "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
    for (var i = 0; i < 5; i++) {
      uniqueId += selection.charAt(
        Math.floor(Math.random() * selection.length)
      );
    }
    this.setState({ 
      uniqueId: uniqueId 
    }, () => this.updateElements());
  }
  updateElements() {
    const container = document.querySelector(
      `#container-${this.state.uniqueId}`
    );
    const placeholder = document.querySelector(
      `#placeholder-${this.state.uniqueId}`
    );
    const image =
      document.querySelector(`#image-${this.state.uniqueId}`);
    image.onload = () => {
      image.setAttribute("style", "opacity: 1");
      container.setAttribute("style", "background: none");
      placeholder.setAttribute("style", "display: none");
    };
  }
  componentDidMount() {
    this.generateUnqiueId();
  }
  render() {
    const preload = `${this.props.src}?q=20`;
    const container = `container-${this.state.uniqueId}`;
    const placeholder = `placeholder-${this.state.uniqueId}`;
    const image = `image-${this.state.uniqueId}`;
    return (
      <div id={container} className="container">
        <img 
          id={placeholder} 
          src={preload} 
          className="placeholder" 
        />
        <img
          id={image}
          src={this.props.src}
          className="image"
          alt={this.props.altText}
        />
      </div>
    );
  }
}
export default ImageLoader;

Полный CSS

.container {
  background-color: #f6f6f6;
  background-size: cover;
  background-repeat: no-repeat;
  height: 100%;
  padding: 0 !important;
  position: relative;
  overflow: hidden;
  transition: background 1s linear;
  width: 100%;
}
.image {
  opacity: 0;
  top: 0;
  width: 100%;
  transition: opacity 1s linear;
  z-index: 1;
}
.placeholder {
  filter: blur(50px);
  position: absolute;
  /* this is needed so Safari keeps sharp edges */
  transform: scale(1);
  transition: display 1s linear;
  width: 100%;
}

Вам это помогло?

Тогда хлопни в ладоши или 40! Также не стесняйтесь ковыряться в других моих сообщениях: