Современный способ ленивой загрузки

внешний интерфейс JavaScript

Почему нам нужна ленивая загрузка

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

Как реализовать ленивую загрузку

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

Ленивая загрузка конкретной реализации

Чтобы загрузить изображение в качестве примера, нам нужноimgустановить тегdata-srcсвойство, которое указывает на изображение, которое нам действительно нужно загрузить, иimgизsrcУкажите на изображение по умолчанию, если оно пустое, оно также отправит запрос на сервер.

<img src="default.jpg" data-src="www.example.com/1.jpg">

Затем, когда пользователь получает доступ к видимой областиimgэлемент,srcзначение заменяется наdata-srcизображение, указывающее на фактически загруженный ресурс

специальный код

const lazy = (el) => {
 let scrTop = getTop();
 let windowHeight = document.documentElement.clientHeight;
 function getTop(){
  return document.documentElement.scrollTop || document.body.scrollTop; 
 }
 function getOffset(node){
  return node.getBoundingClientRect().top + scrTop;
 }
 function inView(node){
  // 设立阈值
 const threshold = 0;
 const viewTop = scrTop;
 const viewBot = viewTop + windowHeight;

 const nodeTop = getOffset(node);
 const nodeBot = nodeTop + node.offsetHeight;

 const offset = (threshold / 100) * windowHeight;
 console.log((nodeBot >= viewTop - offset), (nodeTop <= viewBot + offset))
    return (nodeBot >= viewTop - offset) && (nodeTop <= viewBot + offset)
 }
 function check(node){
   let el = document.querySelector(node);
   let images = [...el.querySelectorAll('img')];
   images.forEach(img => {
    if(inView(img)){
     img.src = img.dataset.src;
    }
   })
 }
 check(el);
}

window.onscroll = function(){
 lazy('.foo');
}

Современный метод реализации ленивой загрузки

Благодаря реализации приведенного выше примера нам нужно отслеживать событие прокрутки, чтобы добиться ленивой загрузки.Хотя мы можем предотвратить высокочастотное выполнение функций с помощью регулирования функции, нам все равно нужно вычислитьscrollTop,offsetHeightи другие атрибуты, есть ли простой способ не вычислять эти атрибуты?Ответ да---IntersectionObserver

Согласно МДН:

API IntersectionObserver предоставляет разработчикам возможность асинхронно наблюдать за пересечением целевого элемента и его предков или окон просмотра. Элемент-предок и окно просмотра называются корнем.

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

При инициализации IntersectionObserver предоставляется конфигурация трех основных элементов:

  • root: это корневой элемент для просмотра. Он определяет базовую структуру захвата для наблюдаемых элементов по умолчанию,rootУказывает на область просмотра браузера, но на самом деле может быть любым элементом DOM с оговоркой, что:rootВ этом случае требуемый элемент для наблюдения за элементом должен находиться внутри элемента Dom, представленного корнем.
  • rootMargin: прямоугольное смещение, добавляемое к ограничивающей рамке корневой (корневой) ограничивающей рамки при расчете пересечения, что может эффективно уменьшить или расширить диапазон определения корня для удовлетворения потребностей расчета. Стоит вариант сmarginCSS похож, напримерrootMargin: '50px 20px 10px 40px'(top, right, bottom, left)
  • порог: список порогов в порядке возрастания, где каждый порог в списке представляет собой отношение площади пересечения к граничной области прослушиваемого объекта. Уведомление генерируется при пересечении любого из порогов объекта прослушивания. Если конструктор не передает значение, значение по умолчанию равно 0.
    рассказать намintersectionObserverМы хотим настроить, нам просто нужно установить нашconfigОбъект передается в конструктор Observer вместе с нашей функцией обратного вызова.
const config = {
    root: null,
    rootMargin: '0px',
    threshold: 0.5
}
let observer = new IntersectionObserver(fucntion(entries){
    // ...
}, config)

Теперь нам нужно датьIntersectionObserverФактически наблюдаемые элементы

const img = document.querySelector('image');
observer.observe(img);

Несколько замечаний об этом элементе фактического наблюдения

  • Сначала он должен быть вrootэлемент DOM, представленный
  • IntersectionObserverМожет принимать только элемент наблюдения, он не поддерживает пакетное наблюдение. Это означает, что если вам нужно соблюдать несколько элементов (например, несколько изображений на странице), вы должны пройти все элементы и наблюдать за каждым из них отдельно
const images = document.querySelecttorAll('img');
images.forEach(image => {
    observer.observe(image)
})
  • При использовании Observer для загрузки страницы вы можете заметить, что были запущены обратные вызовы для всех наблюдаемых элементов IntersectionObserver. Мы можем решить эту проблему с помощью функции обратного вызова

Функция обратного вызова IntersectionObserver

new IntersectionObserver(function(entries, self))

существуетentriesМы получаем нашу функцию обратного вызова, поскольку Array — это особый тип:IntersectionObserverEntryпервыйIntersectionObserverEntryСодержит информацию для трех разных прямоугольников

  • Rootbounds: «Захват кадра (root + rootMargin)' прямоугольник
  • boundClientRect: прямоугольник самого наблюдаемого элемента
  • пересечениеRect: прямоугольник, который фиксирует пересечение фрейма и элемента просмотра.
    также,IntersectionObserverEntryтакже обеспечиваетisIntersecting, удобное свойство, которое возвращает, пересекает ли наблюдаемый элемент кадр захвата, Кроме того,IntersectionObserverEntryОбеспечивает удобные для вычислений свойства обходаintersctionRatio: Возвращает отношение пересеченияRect и boundingClientRect.
    targetзатем возвращает элемент для наблюдения Отлично

После краткого вступления вернемся к основной теме, воспользуемся этимIntersectionObserverДавайте реализуем современный ленивый метод загрузки

const images = document.querySelectorAll('[data-src]')
const config = {
    rootMargin: '0px',
    threshold: 0
};
let observer = new IntersectionObserver((entries, self)=>{
    entries.forEach(entry => {
        if(entry.isIntersecting){
         // 加载图像
         preloadImage(entry.target);
         // 解除观察
           self.unobserve(entry.target)
        }
    })
}, config)

images.forEach(image => {
  observer.observe(image);
});

function preloadImage(img) {
  const src = img.dataset.src
  if (!src) { return; }
  img.src = src;
}

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

Преимущества задержки нагрузки

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

Ссылаться на:

MDN

Now You See Me: How To Defer, Lazy-Load And Act With IntersectionObserver

What is Lazy Loading?