图片懒加载
1.什么是懒加载
懒加载也叫延迟加载,指的是在长网页中延迟加载图像,是一种很好优化网页性能的方式。用户滚动到它们之前,可视区域外的图像不会加载。这与图像预加载相反,在长网页上使用延迟加载将使网页加载更快。在某些情况下,它还可以帮助减少服务器负载。常适用图片很多,页面很长的电商网站场景中。
2.为什么要用懒加载
- 能提升用户的体验,不妨设想下,用户打开像手机淘宝长页面的时候,如果页面上所有的图片都需要加载,由于图片数目较大,等待时间很长,用户难免会心生抱怨,这就严重影响用户体验。
- 减少无效资源的加载,这样能明显减少了服务器的压力和流量,也能够减小浏览器的负担。
- 防止并发加载的资源过多会阻塞js的加载,影响网站的正常使用。
3.懒加载的原理
首先将页面上的图片的 src 属性设为空字符串,而图片的真实路径则设置在data-original属性中, 当页面滚动的时候需要去监听scroll事件,在scroll事件的回调中,判断我们的懒加载的图片是否进入可视区域,如果图片在可视区内将图片的 src 属性设置为data-original 的值,这样就可以实现延迟加载。
4.懒加载实现步骤
// 问题:请解释 document.documentElement.clientHeight 的作用,它和 window.innerHeight 有什么区别?
// 答案:document.documentElement.clientHeight 表示 HTML 文档根元素(<html>)的内部高度,不包含滚动条、边框和外边距。
// 而 window.innerHeight 表示浏览器视口的高度,包含滚动条的宽度。在标准模式下,两者通常相等,但在怪异模式下可能不同。
const viewHeight = document.documentElement.clientHeight;
// console.log(viewHeight); 判断image-item是否到达了加载时刻
// 问题:请说明 querySelectorAll 方法的作用,以及 `img[data-original][lazyload]` 选择器的含义。
// 答案:querySelectorAll 方法用于返回文档中匹配指定 CSS 选择器的所有元素,返回值是一个 NodeList 对象。
// `img[data-original][lazyload]` 选择器表示选择同时包含 data-original 和 lazyload 属性的所有 img 元素。
const eles = document.querySelectorAll("img[data-original][lazyload]");
// console.log(eles);
// 问题:请解释函数表达式 `const lazyload = function ()` 的特点,它和箭头函数有什么区别?
// 答案:函数表达式 `const lazyload = function ()` 是一种匿名函数赋值给变量的方式,存在变量提升但函数体不会提升。
// 与箭头函数的主要区别在于:箭头函数没有自己的 this、arguments、super 和 new.target 绑定,
// 其 this 值继承自外层作用域;而普通函数的 this 值在调用时确定。此外,箭头函数不能作为构造函数使用。
const lazyload = function () {
// 将数组上的prototype方法借给eles 类数组
// 问题:为什么要使用 Array.prototype.forEach.call 来遍历类数组对象,直接使用 forEach 可以吗?
// 答案:因为 `querySelectorAll` 返回的是 NodeList 对象,它是类数组对象,没有数组原型上的 forEach 方法。
// 所以需要使用 `Array.prototype.forEach.call` 将数组的 forEach 方法借给 NodeList 对象使用。直接使用 forEach 会报错。
Array.prototype.forEach.call(eles, function (item, index) {
// console.log(item);
// 没有值, 退出 data-original item.dataset.original
// 问题:请说明 dataset 属性的作用,它和 getAttribute 方法有什么区别?
// 答案:dataset 属性用于访问元素的自定义数据属性(以 data- 开头的属性),它返回一个 DOMStringMap 对象,
// 可以通过驼峰命名法访问这些属性。getAttribute 方法可以获取元素的任意属性值,需要传入完整的属性名。
// dataset 更方便处理自定义数据属性,且代码可读性更高,而 getAttribute 更通用。
if (item.dataset.original === "") return;
// img 标签对象在视口的位置
// 问题:请解释 getBoundingClientRect 方法的返回值,如何利用这些值判断元素是否在视口中?
// 答案:getBoundingClientRect 方法返回一个 DOMRect 对象,包含元素相对于视口的位置信息,
// 如 left、top、right、bottom、width 和 height。若元素的 bottom 大于等于 0 且 top 小于视口高度,
// 则可认为元素进入了视口。
const rect = item.getBoundingClientRect(); // 用于获得页面中某个元素的左,上,右和下分别相对浏览器视窗的位置
// console.log(rect);
// 问题:请解释 `rect.bottom >= 0 && rect.top < viewHeight` 这个条件的含义,它是如何判断元素进入视口的?
// 答案:`rect.bottom >= 0` 表示元素底部在视口顶部及以下;`rect.top < viewHeight` 表示元素顶部在视口底部以上。
// 当这两个条件同时满足时,说明元素至少有一部分在视口中。
if (rect.bottom >= 0 && rect.top < viewHeight) {
(function () {
// 问题:请说明为什么要创建一个新的 Image 对象,它的作用是什么?
// 答案:创建新的 Image 对象是为了在内存中预加载图片。通过设置其 src 属性,可以触发图片的加载过程,
// 并利用 onload 事件监听图片是否加载完成,避免直接给页面中的 img 元素设置 src 时出现加载失败或卡顿的情况。
var img = new Image(); // 内存的img对象
// document.body.appendChild(img)
// 问题:为什么要先将新 Image 对象的 src 属性赋值为 data-original 的值?
// 答案:将新 Image 对象的 src 属性赋值为 data-original 的值,是为了在内存中预加载图片。
// 当图片加载完成后,再将其赋值给页面中的 img 元素,这样可以确保图片加载完成后再显示,避免出现空白或加载失败的情况。
img.src = item.dataset.original;
// 问题:请解释 onload 事件的作用,以及它在图片懒加载中的意义。
// 答案:onload 事件在 Image 对象的图片加载完成后触发。在图片懒加载中,利用这个事件可以确保图片完全加载后,
// 再将其赋值给页面中的 img 元素,避免出现图片未加载完成就显示的情况,提升用户体验。
img.onload = function () {
// 加载完成
// cache 缓存
// 异步
// 问题:请说明为什么要在图片加载完成后再将 src 属性赋值给图片元素?
// 答案:在图片加载完成后再赋值给页面中的 img 元素,可以避免用户看到图片加载过程中的空白或加载失败的情况,
// 提升页面的视觉效果和用户体验。
item.src = item.dataset.original; // 图片大,网速慢
// 问题:请解释 removeAttribute 方法的作用,为什么要移除 data-original 和 lazyload 属性?
// 答案:removeAttribute 方法用于移除元素的指定属性。移除 data-original 和 lazyload 属性是为了优化性能,
// 避免在后续的滚动事件中重复处理已经加载过的图片。
item.removeAttribute("data-original"); // 性能,不用再迭代item
item.removeAttribute("lazyload");
};
})();
}
});
};
// 问题:请解释 window.addEventListener("scroll", lazyload) 的作用,这种方式有什么性能问题,如何优化?
// 答案:window.addEventListener("scroll", lazyload) 的作用是给窗口的滚动事件添加一个监听器,
// 当用户滚动页面时触发 lazyload 函数,检查图片是否进入视口并进行加载。这种方式的性能问题在于,
// 滚动事件会频繁触发,导致大量不必要的函数调用,影响页面性能。可以使用节流(throttle)或防抖(debounce)函数进行优化,
// 减少函数的调用频率。
window.addEventListener("scroll", lazyload);
// 问题:请说明 DOMContentLoaded 事件和 load 事件的区别,为什么要在这个事件中调用 lazyload 函数?
// 答案:DOMContentLoaded 事件在 HTML 文档被完全解析并构建成 DOM 树后触发,不等待样式表、图片等资源加载完成;
// 而 load 事件在页面所有资源(包括图片、样式表等)都加载完成后才触发。在 DOMContentLoaded 事件中调用 lazyload 函数,
// 可以在页面基本结构加载完成后就检查是否有图片已经进入视口,提前加载这些图片,提升用户体验。
document.addEventListener("DOMContentLoaded", lazyload);
</script>