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

Но, как обычно, чем быстрее что-то работает, тем быстрее пользователь захочет, чтобы оно работало.

К сожалению, самые красивые части веб-сайта также загружаются медленнее всего: изображения.

Веб-разработчики разработали (HA… поняли?) различные способы значительно улучшить взаимодействие с пользователем при загрузке изображений. Наиболее часто используется Отложенная загрузка.

Что такое ленивая загрузка?

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

Раньше разработчик добавлял обработчики события прокрутки, чтобы проверить, было ли изображение в области просмотра или нет, но сегодня у нас есть кое-что получше: IntersectionObservers!

Как работает Intersection Observer?

Этот новый API позволяет вам наблюдать за элементом и запускать обратный вызов каждый раз, когда его пересечение с областью просмотра изменяется (да, вы также получаете событие, когда элемент покидает область просмотра). Как это работает? Это очень просто:

Сначала мы объявляем нового наблюдателя пересечения. Здесь мы должны указать обратный вызов и некоторые параметры:

const options = {};
const observer = new IntersectionObserver((entries, obs) => {
    for (const entry of entries) {
        //entry.boundingClientRect
        //entry.intersectionRatio
        //entry.intersectionRect
        //entry.isIntersecting
        //entry.rootBounds
        //entry.target
        //entry.time
    }
}, options);

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

Записи: это список всех элементов наблюдателя dom этим наблюдателем. Каждая запись будет иметь следующие свойства:

  • boundingClientRect: реакция вокруг элемента, видимого внутри области просмотра.
  • пересечениеRatio: какая часть компонента видна внутри области просмотра
  • isIntersecting: самоочевидно….true, если элемент пересекается с областью просмотра, в противном случае — false
  • rootBounds: если корневой элемент объявлен внутри параметров, boundingClientRect с корневым элементом
  • цель: сам элемент
  • время: время, в которое было записано пересечение

Обзоры: это сам наблюдатель, его можно использовать для изменения поведения наблюдателя после запуска одного «наблюдателя».

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

Примечание. Одно небольшое предостережение API-интерфейса наблюдателя пересечения заключается в том, что в первый раз каждый наблюдаемый элемент будет запускать обратный вызов, даже если он находится за пределами экрана.

Хорошо, но как применить это к реакции?

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

import React, { useEffect, useRef, useState } from 'react';
import placeholder from '../imgs/placeholder.png';
const LazyImg = props => {
    const [inView, setInView] = useState(false);
    const placeholderRef = useRef();
    useEffect(() => {
        const observer = new IntersectionObserver((entries, obs) => {
        for (const entry of entries) {
            if (entry.isIntersecting) {
                setInView(true);
                obs.disconnect();
            }
        }
   }, {});
    observer.observe(placeholderRef.current);
    return () => {
        observer.disconnect();
    }
}, []);
return (
    inView ? <img {...props} alt={props.alt || ""} /> : <img {...props} ref={placeholderRef} src={placeholder} alt={props.alt || ""} />
)
};
export default LazyImg;

Давайте посмотрим, как это работает: этот компонент будет использовать одну переменную состояния: inView. Также будет использовать ссылку для ссылки на imgs-заполнитель (это просто прозрачное изображение, которое у нас будет в наших активах). Будучи инициализированным значением false при первом монтировании компонента, он покажет изображение заполнителя с теми же реквизитами, которые вы передали бы тегу img. Затем запустится useEffect и создаст IntersectionObserver, который будет наблюдать за изображением-заполнителем. Обратный вызов также очень прост: всякий раз, когда вызывается обратный вызов, он будет перебирать все записи (мы можем быть уверены, что это будет только один элемент-заполнитель) и проверять, не пересекаются ли они с областью просмотра. Если это так, для состояния inView будет установлено значение true, а затем отключен наблюдатель (чтобы он больше никогда не срабатывал). Наконец, эффект использования вернет функцию (которая будет вызываться onUnmount для отключения наблюдателя.

Если вы используете этот компонент вместо простого тега ‹img ›, вы можете легко проверить вкладку сети в своем браузере, чтобы увидеть, что он фактически выполняет запрос на загрузку изображения только тогда, когда оно появляется в поле зрения.

Выводы и небольшой дополнительный совет

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

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