一、内容概述
图片懒加载技术,通俗点来说,就是只有当用户滚动到页面的某部分,需要显示的图片才会被加载。这样可以减少初次加载页面时的请求量,加快页面加载速度,并减少带宽使用,从而优化用户体验。
二、代码详解
学习笔记已以注释的形式随在各个重要的代码后。
首先是HTML部分
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>图片懒加载学习笔记</title>
</head>
<body>
<!-- 假设的图片列表,使用data-src代替src -->
<!-- data-src的值会在懒加载函数中赋值给src,从而完成图片加载 -->
<!-- 这里把需要懒加载的图片的类统一设置成lazyload,以便于展开下面的DM操作 -->
<img class="lazyload" data-src="image1.jpg" alt="image1">
<img class="lazyload" data-src="image2.jpg" alt="image2">
<img class="lazyload" data-src="image3.jpg" alt="image3">
<!-- 脚本文件 -->
<script type="text/javascript">
// 这里将呈现我们的懒加载代码
</script>
</body>
</html>
然后是JS部分。JS中的代码填充到上面的script中
// 这段代码保证了整个script只有在DOM加载完成后才会执行
document.addEventListener("DOMContentLoaded", function() {
// 设置的参数"DOMContentLoaded",是一个事件
// 该事件是在HTML文档被完全加载和解析后立即触发
// 不需要等待样式表、图片这些加载完成
let lazyloadImages = document.querySelectorAll("img.lazyload");
// 设置变量来搜寻所有img标签中的lazyload类,即查询所有的需要懒加载的图片
//这些图片初始时不会加载其src指定的内容,而是等待特定条件触发加载
let lazyloadThrottleTimeout;
// 该变量用来实现节流功能,具体原因和实现原理我写在用到他的部分
// 检查图片是否在可视区域的函数
function lazyload() {
// 函数思路:该函数检查每个需要懒加载的图片是否进入用户可视区域。
// 若是,则将data-src的值赋值给src,进行图片加载
// 并移除lazyload类标记图片已经加载了
if(lazyloadThrottleTimeout) {
// 节流
clearTimeout(lazyloadThrottleTimeout);
}
lazyloadThrottleTimeout = setTimeout(function() {
// 设置节流的原因:
// 如果不使用节流函数,那么滚动、窗口大小变化或设备方向变化时,lazyload函数会在每次事件触发时立即执行。
// 考虑到这些事件可能会以非常高的频率连续触发(尤其是滚动事件),
// lazyload函数的频繁执行可能会导致服务器资源浪费、增加性能负担等问题
// 节流实现原理:
// 节流函数通过设置一个定时器(setTimeout)来延迟函数的执行,并在延迟期间内忽略接下来触发的函数调用请求。
// 在这段代码中,lazyload 函数被设置为在滚动、窗口大小变化或设备方向变化时调用,但实际上,它通过setTimeout延迟20毫秒执行。
// 如果在这20毫秒内事件再次被触发(例如用户持续滚动页面),则通过clearTimeout取消之前设置的定时器
// 并重新设置一个新的定时器。这样做的结果是lazyload函数在短时间内不会被连续调用,而是每20毫秒最多调用一次。
let scrollTop = window.pageYOffset;
lazyloadImages.forEach(function(img) {
if(img.offsetTop < (window.innerHeight + scrollTop)) {
// 用户的某些操作使得图片进入用户视线范围
img.src = img.dataset.src;
img.classList.remove('lazyload');
// 移除标签
}
});
if(lazyloadImages.length == 0) {
document.removeEventListener("scroll", lazyload);
window.removeEventListener("resize", lazyload);
window.removeEventListener("orientationChange", lazyload);
}
}, 20);
}
// 使用Intersection Observer API
if ("IntersectionObserver" in window) {
// 如果浏览器支持IntersectionObserver API
// 将使用此更现代的方法来观察图片是否进入可视区域。
let observer = new IntersectionObserver(function(entries, observer) {
entries.forEach(function(entry) {
// 异步检测目标元素与其祖先元素或顶级文档视窗(viewport)交叉状态
if (entry.isIntersecting) {
let img = entry.target;
img.src = img.dataset.src;
img.classList.remove("lazyload");
observer.unobserve(img);
}
});
});
lazyloadImages.forEach(function(img) {
observer.observe(img);
});
} else {
// 备用方案:监听滚动事件
// 对于不支持IntersectionObserver的浏览器
// 代码回退到监听scroll、resize和orientationChange事件
// 并在这些事件发生时执行懒加载函数
document.addEventListener("scroll", lazyload);
window.addEventListener("resize", lazyload);
window.addEventListener("orientationChange", lazyload);
// 确保了无论用户是通过滚动、调整窗口大小还是改变设备方向
// 只要图片进入可视区域,就会触发加载。
}
});
三、没有注释的简洁代码
document.addEventListener("DOMContentLoaded", function() {
let lazyloadImages = document.querySelectorAll("img.lazyload");
let lazyloadThrottleTimeout;
function lazyload() {
if(lazyloadThrottleTimeout) {
clearTimeout(lazyloadThrottleTimeout);
}
lazyloadThrottleTimeout = setTimeout(function() {
let scrollTop = window.pageYOffset;
lazyloadImages.forEach(function(img) {
if(img.offsetTop < (window.innerHeight + scrollTop)) {
img.src = img.dataset.src;
img.classList.remove('lazyload');
}
});
if(lazyloadImages.length == 0) {
document.removeEventListener("scroll", lazyload);
window.removeEventListener("resize", lazyload);
window.removeEventListener("orientationChange", lazyload);
}
}, 20);
}
if ("IntersectionObserver" in window) {
let observer = new IntersectionObserver(function(entries, observer) {
entries.forEach(function(entry) {
if (entry.isIntersecting) {
let img = entry.target;
img.src = img.dataset.src;
img.classList.remove("lazyload");
observer.unobserve(img);
}
});
});
lazyloadImages.forEach(function(img) {
observer.observe(img);
});
} else {
document.addEventListener("scroll", lazyload);
window.addEventListener("resize", lazyload);
window.addEventListener("orientationChange", lazyload);
}
});