前言
在之前的文章十分钟搞懂前端高频考点:图片懒加载中我提到了如何手搓一个图片懒加载,当时还自以为是地优化了一下,觉得这样已经足够优雅了,直到看到了另外两位大佬的指点,我才发现还有许多不足之处。既然有大佬指点,那当然是再好不过,按捺住鸡冻
的心,颤抖的手,我立马加深了对图片懒加载的学习,今天继续往下聊聊,另外两种能够更优雅地实现图片懒加载的方法。
正文
在之前的文章中已经提到了什么是图片懒加载,在这里就不过多赘述了。今天介绍的两种方法分别是loading和IntersectionObserver
Loading
作为一个HTML5中新增加的属性,默认情况下的值为eager,但将img标签的loading属性设置为lazy之后,浏览器会自动将图片视为懒加载图片,也就是当浏览器进入视窗时才会加载,而未进入视窗的图片则不加载。loading MDN文档
html
<img src="fakelocation" loading="lazy" alt="">
这一属性大大降低的图片懒加载的复杂度,使开发者不再需要编写复杂的js代码,同时,也避免了过度监听滚动事件从而造成大量的回调函数执行,优化了性能。
什么是IntersectionObserver
聊完loading再来聊聊IntersectionObserver。loading已经可以说是无可挑剔了,但依旧存在浏览器不支持的情况。那么这个时候,我们可以用IntersectionObserver去更优雅地实现图片懒加载。这个方法虽然更复杂一些,甚至比手搓一个都复杂,但是不得不承认在性能优化上要比原生js来的更好。
IntersectionObserver 是一种浏览器提供的API,用于异步观察目标元素与视口(viewport)或其他元素的交叉状态变化。这个API特别适用于提升网页性能和实现特定的交互效果,也就是说除了懒加载图片,还可以实现无限滚动或者在元素进入可视区域时启动动画等。
IntersectionObserver的使用
IntersectionObserver 是一个构造函数,使用前我们需要将其实例化,在实例化的过程中,添加一个回调函数作为参数,当观察的目标元素进入或退出视口,或者与设定的根元素发生交叉时,该回调函数会被触发。这个过程是异步的,并且只有在浏览器空闲时才会执行,这也就是为什么相比使用scroll事件监听等方法,它能更高效地利用资源,减少页面重绘和回流,从而提升用户体验和页面性能。
js
document.addEventListener('DOMContentLoaded', function () {
// 创建IntersectionObserver实例
const observer = new IntersectionObserver((entries, observer) => {
entries.forEach(entry => {
// 如果元素与视口相交
if (entry.isIntersecting) {
// 获取当前图片元素的data-src属性并设置为src属性,实现图片加载
const img = entry.target;
const src = img.getAttribute('data-src');
// 设置src属性,触发图片加载
img.src = src;
// 加载完成后,可以停止观察此元素
observer.unobserve(img);
}
});
});
// 选择所有需要懒加载的图片元素并开始观察它们
const lazyImages = document.querySelectorAll('.lazy-image');
lazyImages.forEach(img => {
observer.observe(img);
});
});
作为参数的回调函数接受两个参数,第一个是entries,代表所有被观测的元素,是一个数组。而第二个observer则是观察器自身,通常情况下并不需要使用所以也就不做过多介绍了(其实就是我太菜学不明白,只知道可以修改观察器的配置,解除对某些元素的观察。欢迎各位读者大佬指点。)
值得一提的是,IntersectionObserver构造函数还可以接受第二个参数作为其配置项,控制图片开始加载的时刻,参数的数据类型是对象,其中有两个键值对分别是rootMargin和threshold,用法如下
js
rootMargin: '0px',// 配置选项,比如阈值,默认为0,即只要元素有一部分可见就触发
threshold: 0.1 // 可以设置多个阈值,这里表示元素至少10%可见时就触发
如此一来,通过IntersectionObserver就可以实现图片懒加载了。
总结
通常情况下,我个人肯定更倾向于使用loading标签,但是由于兼容性问题,我们需要在开发过程中多做一些准备,确保图片懒加载能够如期进行,以下是完整的代码
js
// 检查浏览器是否支持原生的 lazy loading
function supportsLazyLoading() {
const img = new Image();
return 'loading' in img;
}
// 如果浏览器不支持原生lazy loading,则使用JavaScript实现懒加载
function enableLazyLoadingFallback() {
let images = document.querySelectorAll('img[data-src][loading="lazy"]');
let viewHeight = window.innerHeight;
let intersectionObserverSupported = ('IntersectionObserver' in window);
function loadImage(img) {
img.src = img.dataset.src;
img.removeAttribute('loading');
img.removeAttribute('data-src');
}
if (intersectionObserverSupported) {
const observer = new IntersectionObserver((entries, observer) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
loadImage(entry.target);
observer.unobserve(entry.target);
}
});
});
images.forEach(img => {
observer.observe(img);
});
} else {
// 对于不支持IntersectionObserver的浏览器,可以考虑使用scroll事件的简单监听
// 注意:这种方式效率较低,可能引起性能问题
window.addEventListener('scroll', () => {
images.forEach(img => {
if (img.getBoundingClientRect().top < viewHeight + window.scrollY) {
loadImage(img);
}
});
});
}
}
if (!supportsLazyLoading()) {
enableLazyLoadingFallback();
}
作为一个小白,我自知掌握的深度有限,因此也恳请各位读者姥爷能够多加指点。最后祝各位0 waring(s),0 error(s)。