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

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

Эта статья включена в githubGitHub.com/Майкл-Ли Чжиган…

адрес исходного кода демоGitHub.com/Майкл-Ли Чжиган…

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

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

Давайте сначала посмотрим на информацию об изображении, загружаемую при запуске страницы.

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

Из вышеизложенного видно, что некоторые картинки достигают сотен кБ, а настройка стоит 2М (этот горшок надо оперировать назад, а большие картинки высокого разрешения загружать?), что напрямую приводит к длительному времени загрузки.

Ввиду описанной выше ситуации ленивая загрузка изображений имеет следующие преимущества:

  1. Уменьшите загрузку ресурсов, и загружайте картинки только на первом экране при запуске страницы, что позволяет значительно снизить нагрузку и трафик на сервер, а также снизить нагрузку на браузер.
  2. Предотвратите одновременную загрузку слишком большого количества ресурсов и заблокируйте загрузку js, что повлияет на запуск всего веб-сайта.
  3. Это может улучшить взаимодействие с пользователем.Представьте, что когда пользователь открывает страницу, если все изображения на странице должны быть загружены, из-за большого количества изображений время ожидания очень велико, что серьезно повлияет на взаимодействие с пользователем. .

Принцип ленивой загрузки картинок

Принцип ленивой загрузки изображений в основном реализуется основной логикой оценки того, достигло ли текущее изображение видимой области.

  1. Получить все фотографии купола.
  2. Пройдитесь по каждому изображению, чтобы определить, находится ли текущее изображение в видимой области.
  3. Если это так, установите атрибут src изображения.
  4. привязать окноscrollсобытие и отслеживать его для событий.

Давайте сначала посмотрим на структуру страницы

<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>Lazyload</title>
    <style>
      img {
        display: block;
        margin-bottom: 50px;
        height: 200px;
        width: 400px;
      }
    </style>
  </head>
  <body>
    <img src="./img/default.png" data-src="./img/1.jpg" />
    <img src="./img/default.png" data-src="./img/2.jpg" />
    <img src="./img/default.png" data-src="./img/3.jpg" />
    <img src="./img/default.png" data-src="./img/4.jpg" />
    <img src="./img/default.png" data-src="./img/5.jpg" />
    <img src="./img/default.png" data-src="./img/6.jpg" />
    <img src="./img/default.png" data-src="./img/7.jpg" />
    <img src="./img/default.png" data-src="./img/8.jpg" />
    <img src="./img/default.png" data-src="./img/9.jpg" />
    <img src="./img/default.png" data-src="./img/10.jpg" />
  </body>
</html>

Сначала получите дом всех картинок, пройдитеdocument.body.clientHeightПолучите высоту видимой области, а затем используйтеelement.getBoundingClientRect()API напрямую получает верхнее значение относительного просмотра элемента и просматривает каждое изображение, чтобы определить, находится ли текущее изображение в видимой области. код показывает, как показано ниже:

function lazyload() {
  let viewHeight = document.body.clientHeight //获取可视区高度
  let imgs = document.querySelectorAll('img[data-src]')
  imgs.forEach((item, index) => {
    if (item.dataset.src === '') return

    // 用于获得页面中某个元素的左,上,右和下分别相对浏览器视窗的位置
    let rect = item.getBoundingClientRect()
    if (rect.bottom >= 0 && rect.top < viewHeight) {
      item.src = item.dataset.src
      item.removeAttribute('data-src')
    }
  })
}

Наконец привязать к окнуonscrollсобытие

window.addEventListener('scroll', lazyload)

Главное — выполнить операцию ленивой загрузки изображения. Но есть большая проблема с производительностью, потому чтоscrollСобытие будет срабатывать много раз за короткий промежуток времени, что серьезно влияет на производительность страницы.Чтобы улучшить производительность страницы, нам нужна функция дросселирования, чтобы контролировать многократное срабатывание функции, и выполнять только обратный вызов один раз в течение определенного периода времени (например, 200 мс).

Следующее реализует функцию дросселя

function throttle(fn, delay) {
  let timer
  let prevTime
  return function (...args) {
    const currTime = Date.now()
    const context = this
    if (!prevTime) prevTime = currTime
    clearTimeout(timer)

    if (currTime - prevTime > delay) {
      prevTime = currTime
      fn.apply(context, args)
      clearTimeout(timer)
      return
    }

    timer = setTimeout(function () {
      prevTime = Date.now()
      timer = null
      fn.apply(context, args)
    }, delay)
  }
}

затем измените егоsrcollсобытие

window.addEventListener('scroll', throttle(lazyload, 200))

IntersectionObserver

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

IntersectionObserver— это новый API, который автоматически «следит», если элемент виден, Chrome 51+ уже поддерживает его. Поскольку сущность visible заключается в том, что целевой элемент и область просмотра создают пересечение, этот API называется «средством просмотра пересечений». Давайте посмотрим на его использование:

var io = new IntersectionObserver(callback, option)

// 开始观察
io.observe(document.getElementById('example'))

// 停止观察
io.unobserve(element)

// 关闭观察器
io.disconnect()

IntersectionObserverЭто конструктор, изначально предоставляемый браузером и принимающий два параметра: callback — это функция обратного вызова при изменении видимости, а option — объект конфигурации (этот параметр является необязательным).

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

var io = new IntersectionObserver((entries) => {
  console.log(entries)
})

Параметры функции обратного вызова(entries)представляет собой массив, каждый элемент которого являетсяIntersectionObserverEntryобъект. Например, если видимость двух наблюдаемых объектов меняется одновременно,entriesМассив будет состоять из двух элементов.

  • время: время, когда изменилась видимость, это высокоточная временная метка в миллисекундах.
  • цель: целевой элемент, за которым нужно наблюдать, является объектом узла DOM.
  • isIntersecting: видна ли цель
  • rootBounds: Информация о прямоугольной области корневого элемента,getBoundingClientRect()Возвращаемое значение метода или null, если нет корневого элемента (т.е. прокрутка непосредственно относительно окна просмотра)
  • boundingClientRect: информация о прямоугольной области целевого элемента
  • пересечениеRect: Информация о области пересечения целевого элемента и области просмотра (или корневого элемента)
  • пересечениеRatio: видимое соотношение целевого элемента, т.е.intersectionRectЗаниматьboundingClientRectМасштаб , 1, когда полностью виден, и меньше или равен 0, когда полностью невидим

Ниже мы используемIntersectionObserverРеализовать ленивую загрузку изображений

const imgs = document.querySelectorAll('img[data-src]')
const config = {
  rootMargin: '0px',
  threshold: 0,
}
let observer = new IntersectionObserver((entries, self) => {
  entries.forEach((entry) => {
    if (entry.isIntersecting) {
      let img = entry.target
      let src = img.dataset.src
      if (src) {
        img.src = src
        img.removeAttribute('data-src')
      }
      // 解除观察
      self.unobserve(entry.target)
    }
  })
}, config)

imgs.forEach((image) => {
  observer.observe(image)
})

ленивые инструкции по загрузке

Во Vue, помимо обычногоv-show,v-bind,v-forПомимо других команд, вы также можете настраивать команды. Функция определения директивы Vue предоставляет несколько функций ловушек (необязательно):

  • bind: вызывается только один раз, когда директива привязывается к элементу в первый раз, вы можете определить действие инициализации, которое выполняется один раз во время привязки.
  • вставленный: вызывается, когда связанный элемент вставляется в родительский узел (родительский узел может быть вызван, если он существует, он не обязательно должен существовать в документе).
  • update: вызывается при обновлении шаблона, содержащего связанный элемент, независимо от того, изменилось ли связанное значение. Путем сравнения значений привязки до и после обновления.
  • componentUpdated: вызывается, когда шаблон, содержащий связанный элемент, завершает цикл обновления.
  • unbind: вызывается только один раз, когда инструкция отсоединяется от элемента.

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

  1. Проверьте, поддерживает ли его браузерIntersectionObserverAPI, используйте, если поддерживаетсяIntersectionObserverРеализуйте ленивую загрузку, в противном случае используйтеsrcollМетод реализации мониторинга событий + троттлинг.
  2. пройти черезVue.directiveЗарегистрироватьv-lazyдиректива, раскрывающаяinstall()функция для вызова Vue.
  3. существуетmain.jsВ эксплуатации (инструкция) можно назвать.
  4. внутри компонента<img>помеченsrcзаменитьv-lazyЛенивая загрузка изображений может быть достигнута.

код показывает, как показано ниже

новыйLazyLoad.jsдокумент

const LazyLoad = {
  // install方法
  install(Vue, options) {
    const defaultSrc = options.default
    Vue.directive('lazy', {
      bind(el, binding) {
        LazyLoad.init(el, binding.value, defaultSrc)
      },
      inserted(el) {
        if (IntersectionObserver) {
          LazyLoad.observe(el)
        } else {
          LazyLoad.listenerScroll(el)
        }
      },
    })
  },
  // 初始化
  init(el, val, def) {
    el.setAttribute('data-src', val)
    el.setAttribute('src', def)
  },
  // 利用IntersectionObserver监听el
  observe(el) {
    var io = new IntersectionObserver((entries) => {
      const realSrc = el.dataset.src
      if (entries[0].isIntersecting) {
        if (realSrc) {
          el.src = realSrc
          el.removeAttribute('data-src')
        }
      }
    })
    io.observe(el)
  },
  // 监听scroll事件
  listenerScroll(el) {
    const handler = LazyLoad.throttle(LazyLoad.load, 300)
    LazyLoad.load(el)
    window.addEventListener('scroll', () => {
      handler(el)
    })
  },
  // 加载真实图片
  load(el) {
    const windowHeight = document.documentElement.clientHeight
    const elTop = el.getBoundingClientRect().top
    const elBtm = el.getBoundingClientRect().bottom
    const realSrc = el.dataset.src
    if (elTop - windowHeight < 0 && elBtm > 0) {
      if (realSrc) {
        el.src = realSrc
        el.removeAttribute('data-src')
      }
    }
  },
  // 节流
  throttle(fn, delay) {
    let timer
    let prevTime
    return function (...args) {
      const currTime = Date.now()
      const context = this
      if (!prevTime) prevTime = currTime
      clearTimeout(timer)

      if (currTime - prevTime > delay) {
        prevTime = currTime
        fn.apply(context, args)
        clearTimeout(timer)
        return
      }

      timer = setTimeout(function () {
        prevTime = Date.now()
        timer = null
        fn.apply(context, args)
      }, delay)
    }
  },
}

export default LazyLoad

существуетmain.jsвнутри директивы использования

import LazyLoad from './LazyLoad.js'

Vue.use(LazyLoad, {
  default: 'xxx.png',
})

внутри компонента<img>помеченsrcзаменитьv-lazy

<img v-lazy="xxx.jpg" />

Таким образом, можно выполнить инструкцию отложенной загрузки vue.

резюме

  1. Ленивая загрузка изображений необходима для повышения производительности загрузки сайта.
  2. Принцип ленивой загрузки картинок заключается в том, чтобы судить о том, загружается ли текущая картинка в видимую область.Соответствующая функция может быть реализована путем прослушивания события прокрутки и IntersectionObserver.
  3. Инструкции по ленивой загрузке изображения можно написать через Vue.directive.

рекомендуемая статья