1. 为什么需要图片懒加载?
原因:
- 当页面加载时,如果所有的图片都立即加载,会导致页面加载时间延长,尤其是对于有大量图片的网页。
- 对于访问网页的用户来说,不是所有的图片都是必须立即加载的.
- 对于网站的服务器来说,减少同时请求的资源数量可以降低服务器的负载。
使用图片懒加载可以解决以上问题
- 提高页面首次加载的速度 懒加载允许推迟加载图片,只有在用户需要查看它们时才加载,从而加速页面的初始加载速度。
- 节省宽带和资源 通过懒加载,可以减少不必要的网络请求,节省带宽,并避免加载用户当前不可见的内容。
- 减少服务器负载 通过推迟加载图片,服务器可以更有效地处理其他请求,提高整体的性能和稳定性。
总体来说就是:
- 通过图片懒加载技术,我们可以优化项目首次打开的时间, 降低服务器的负载(按需请求,而不是一次全部请求完), 同时对用户而言, 可以降低宽带的消耗.
2. 如何实现图片懒加载
2.1 第一种: 使用img 标签的 loading 属性
loading 属性指定浏览器是应立即加载图像还是延迟加载图像。
设置 loading="lazy" 只有鼠标滚动到该图片所在位置才会显示
语法:
html
<img src="URL" loading="eager|lazy">
属性值
值 | 描述 |
eager | 默认,图像立即加载。 |
lazy | 图像延迟加载,只有鼠标滚动到该图片所在位置才会显示。 |
2.2 第二种: 通过js在指定时机设置 img 的 src 属性值
实现步骤:
- 拿到所有图片的dom元素
- 遍历这个含有图片的元素列表是否到达了可视区的范围内
- 如果到了, 旧将该元素的src 属性进行设置
- 监听浏览器的滚动, 并不断执行上面逻辑的函数
前置知识补充 || 温习
(1)获取浏览器窗口高度(可视区域高度)
浏览器窗口高度通过 window.innerHeight
这个 API
来获取
js
const viewportHeight = window.innerHeight
(2)获取元素距离浏览器窗口顶部的高度
获取元素距离可视区域顶部的高度需要通过getBoundingClientRect()
API 来实现,getBoundingClientRect()
获取的是 DOM 元素相对于窗口的坐标集合,集合中有多个属性,其中的 top
属性就是当前元素元素距离窗口可视区域顶部的距离
js
const element = document.getElementById("your-element-id"); const distanceToTop = element.getBoundingClientRect().top;
判断元素是否处于可视区的逻辑
从上面这张图里面,我们就会得知:
- 判断图片是否处于可视区, 就可以根据该元素相对浏览器的top 和 浏览器可视区域的高 做相减
- 如果值为
负数
, 说明该元素处在浏览器的可视区域内
- 如果值为
正数
, 说明该元素于浏览器的可视区域的外面
代码实现
因此我们就可以编写我们的代码了:
- 为了提高网页性能,需要一个节流函数来控制函数的多次触发
- 页面首次完成渲染之后, 调用一次
lazyload 函数
,用于将当前视口的图片给展现出来
html
<body> <img data-src="./image/1.jpg" src="http://iph.href.lu/200x200" alt="" id="one"> <img data-src="./image/2.jpg" src="http://iph.href.lu/200x200" alt=""> <img data-src="./image/3.jpg" src="http://iph.href.lu/200x200" alt=""> <img data-src="./image/3.png" src="http://iph.href.lu/200x200" alt=""> <img data-src="./image/5.jpg" src="http://iph.href.lu/200x200" alt=""> <img data-src="./image/6.jpg" src="http://iph.href.lu/200x200" alt=""> <img data-src="./image/7.jpg" src="http://iph.href.lu/200x200" alt=""> <img data-src="./image/1.jpg" src="http://iph.href.lu/200x200" alt="" id="one"> <img data-src="./image/2.jpg" src="http://iph.href.lu/200x200" alt=""> <img data-src="./image/3.jpg" src="http://iph.href.lu/200x200" alt=""> <img data-src="./image/3.png" src="http://iph.href.lu/200x200" alt=""> <img data-src="./image/5.jpg" src="http://iph.href.lu/200x200" alt=""> <img data-src="./image/6.jpg" src="http://iph.href.lu/200x200" alt=""> <img data-src="./image/7.jpg" src="http://iph.href.lu/200x200" alt=""> <script> // 获取所有img元素 const imgarr = document.querySelectorAll('img'); // 获取浏览器的可视区域的高度 // 获取浏览器的可视区域的高度 const clientHeight = window.innerHeight // 使用 debounce 函数来优化滚动事件 function debounce(func, wait) { let timeout; return function () { const context = this; const args = arguments; clearTimeout(timeout); timeout = setTimeout(function () { func.apply(context, args); }, wait); }; } // 优化后的 lazyload 函数 function lazyload() { for (let i = 0; i < imgarr.length; i++) { const distanceWindowTop = imgarr[i].getBoundingClientRect().top; // 如果图片距离页面顶部的距离小于等于可视区域的高度,就加载图片 if (distanceWindowTop - clientHeight <= 0) { imgarr[i].src = imgarr[i].getAttribute('data-src'); } } } // 添加 debounce 后的滚动事件监听 const debouncedLazyLoad = debounce(lazyload, 200); // 调整需要的等待时间 window.addEventListener('scroll', debouncedLazyLoad); // // 初次加载页面时执行一次 lazyload document.addEventListener('DOMContentLoaded', lazyload); </script> </body>
效果:
2.3 第二种: 使用IntersectionObserver API
介绍:
IntersectionObserver
是一个在浏览器中提供的用于异步观察目标元素与其祖先元素或视口交叉情况的API。它可以有效地用于实现懒加载、无限滚动等场景。
基本概念
- IntersectionObserver 观察器(Observer):
IntersectionObserver
对象是观察器,负责观察目标元素的交叉状态。
- 目标元素(Target):
- 目标元素是你希望观察的 DOM 元素。
- 交叉状态(Intersection):
- 目标元素与其祖先元素或视口的交叉状态,包括进入视口、离开视口等情况。
语法
js
new IntersectionObserver(callback, options);
callback
是一个回调函数,会在目标元素的交叉状态发生变化时被调用。options
是一个配置对象,用于设置观察器的参数,例如threshold
(交叉比例)等。
使用方法
- 创建 IntersectionObserver 对象:
js
const observer = new IntersectionObserver(callback, options);
- 观察目标元素:
js
observer.observe(targetElement);
- 将目标元素传递给
observe
方法,观察器会开始观察该元素。
- 回调函数:
被IntersectionObserver
观察的目标元素,当它们进入或离开视口时,会触发指定的回调函数(callback)。
js
function callback(entries, observer) { entries.forEach(entry => { // 处理每个目标元素的交叉状态变化 if (entry.isIntersecting) { // 目标元素进入视口 } else { // 目标元素离开视口 } }); }
- 回调函数接收一个
entries
数组,每个数组元素是一个IntersectionObserverEntry
对象,表示一个观察的目标元素的状态。 isIntersecting
为true
,则表示目标元素正在视口内;如果为false
,则表示目标元素已经离开视口。observer
参数是一个指向创建该IntersectionObserver
实例的对象的引用。这个参数允许你在回调函数中调用unobserve
方法,以停止观察某个特定的目标元素。
- 取消观察:
js
observer.unobserve(targetElement);
- 如果不再需要观察某个目标元素,可以使用
unobserve
方法取消观察。 - 不再观察当前已经进入视口的目标元素,这是为了提高性能,避免不必要的观察。
实现懒加载
使用IntersectionObserver 进行图片懒加载
js
// 获取所有带有 data-src 属性的 img 元素 const imageArr = document.querySelectorAll('img[data-src]') // IntersectionObserver 的配置项 const config = { threshold: 0.5, // 表示当目标元素的50%进入视口时触发回调 } // 创建 IntersectionObserver 实例 let observer = new IntersectionObserver((entries, observer) => { entries.forEach((entry) => { // 如果目标元素进入视口 if (entry.isIntersecting) { let img = entry.target let src = img.dataset.src // 如果存在 data-src 属性,则加载图片并移除 data-src 属性 if (src) { img.src = src img.removeAttribute('data-src') } // 解除观察 observer.unobserve(entry.target) } }) }, config) // 遍历所有带有 data-src 属性的 img 元素并开始观察 imageArr.forEach((image) => { observer.observe(image) })
效果