Skip to content
大纲

如何实现懒加载?

懒加载最常用在图片上,其他的元素也有使用,比如 iframe,页面甚至组件

  • 图片
    • scroll 实现
    • IntersectionObserver 实现
  • iframe
  • 页面
  • 组件

实现懒加载

[clientHeight、scrollTop 和 offsetTop]

js
// 方案一:clientHeight, scrollTop 和 offsetTop
// 通过监听 scroll 事件来判断图片是否到达视口

let img = document.getElementsByTagName('img')
let num = img.length
let count = 0 // 计数器,从第一张图片开始计

lazyload() // 首次加载别忘了显示图片

window.addEventListener('scroll', lazyload)

function getScrollTop() {
  // 首先尝试使用 window.pageYOffset(兼容大多数主流浏览器)
  // 如果不支持,则尝试 document.documentElement.scrollTop(滚动操作作用于根元素,即 `<html>` 元素)
  // 最后再尝试 document.body.scrollTop,因为在某些老旧的浏览器中,例如 IE8 及更早版本,滚动操作作用于 `<body>` 元素而不是根元素。
  return  window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop;
}

function lazyload() {
  // 视口高度
  let viewHeight = document.documentElement.clientHeight
  // 滚动条已滚动的高度
  let scrollTop = getScrollTop()
  for (let i = count; i < num; i++) {
    // 元素现在已经出现在视口中
    if (img[i].offsetTop < scrollHeight + viewHeight) {
      if (img[i].getAttribute('src') !== 'default.jpg') continue
      img[i].src = img[i].getAttribute('data-src')
      count++
    }
  }
}

// 最好对 scroll 事件做节流处理,以免频繁触发:
window.addEventListener('scroll', throttle(lazyload, 200))
js
// 用另外一种方式来判断图片是否出现在了当前视口,即 DOM 元素的 getBoundingClientRect API
// 根据当前元素的位置和视口进行判断是否要加载这张图片
//    性能优化
//      - 图片全部加载完成后移除事件监听
//      - 加载完的图片,从 imgList 移除,起到优化内存的作用
// https://juejin.cn/post/6844903856489365518#heading-19

let imgList = [...document.querySelectorAll('img')]
let length = imgList.length

const imgLazyLoad = function () {
  let count = 0
  const clientHeight = document.documentElement.clientHeight
  return (function () {
    let deleteIndexList = []
    imgList.forEach((img, index) => {
      let rect = img.getBoundingClientRect()
      if (rect.top < window.innerHeight) {
        // 判断逻辑 也可以使用 clientHeight
        img.src = img.dataset.src
        deleteIndexList.push(index)
        count++
        if (count === length) {
          document.removeEventListener('scroll', imgLazyLoad)
        }
      }
    })
    imgList = imgList.filter((img, index) => !deleteIndexList.includes(index))
  })()
}

// 这里最好加上防抖处理
document.addEventListener('scroll', imgLazyLoad)
js
// lazy-load iframe

// 基础用法
// create a new Intersection Observer
;(function () {
  const callback = () => {}
  const observer = new IntersectionObserver(callback)
})()

// the element that you want to watch
// const element = document.querySelector('iframe');

// register the element with the observe method
// observer.observe(element);
;(function () {
  const observer = new IntersectionObserver(function (entries, observer) {
    entries.forEach((entry) => {
      if (entry.isIntersecting) {
        // 判断是否在视口中
        // do this when the element enters the viewport
        loadElement(entry.target)
        // stop watching
        observer.unobserve(entry.target)
      }
    })
  })

  function loadElement(element) {
    const src = element.getAttribute('data-src')
    element.src = src
  }
})()