原生JS - 图片懒加载@郝晨光


前言

在现在以用户体验至上的前端时代,为了提升页面加载速度,为了提升用户体验,我们经常会用到图片懒加载这个功能。所以总结记录一下图片懒加载的原理以及实现方式
图片懒加载


什么是懒加载?

懒加载其实就是延迟加载,是一种对网页的性能优化的方式,当访问一个页面的时候,优先显示可视区域内的图片,而不是一次性加载所有图片,当需要显示的时候,再进行加载,避免打开网页时加载过多的资源,发送过多的HTTP请求。


实现原理

  1. 标签的src属性就是代表了一个资源引用,它的src就代表了图片资源的路径,如果这个属性不为空,浏览器就会发送请求,如果为空的话就不发送。
  2. 我们就可以利用这一点,在需要的时候,我们再给图片设置src属性。
  3. 其实图片懒加载的原理,就是通过动态的判断图片是否处在可视区域内,如果处在可视区域内的话,就加载当前图片,否则的话,就先不加载。
  4. 我们需要监听浏览器的scroll事件,并在滑动的时候进行判断,当所有图片都加载完成以后,应该清除监听事件。
  5. 对于scroll事件,我们需要实现函数节流的,避免函数重复触发,减少浏览器负荷,提升用户体验。

具体实现

HTML实现
<ul>
	<li>
		<img data-src="./Image%203.png" alt="">
	</li>
	<li>
		<img data-src="./Image%203.png" alt="">
	</li>
	<li>
		<img data-src="./Image%203.png" alt="">
	</li>
	<li>
		<img data-src="./Image%203.png" alt="">
	</li>
	<li>
		<img data-src="./Image%203.png" alt="">
	</li>
	<li>
		<img data-src="./Image%203.png" alt="">
	</li>
</ul>
Javascript实现(第一种方式)

写在前边说一下javascript中的getBoundingClientRect()这个方法
这个方法可以获取到DOM节点距离四周的距离,包含四个属性:top、left、bottom、right,分别代表的是:元素的上边框距离页面顶部的距离、元素的左边框距离页面左边的距离、元素的下边框距离页面底部的距离、元素的右边框距离页面右边的距离。

/**
 * @function throttle 函数节流
 * @param fn 需要函数节流执行的函数
 * @param interval 函数触发的间隔时间
* */
function throttle(fn, interval) {
	// timer 定时器,
	// firstTime判断是否是第一次执行,第一次执行不需要节流
	let timer = null, firstTime = true;
	// 闭包
	return function () {
		// args 保存原有参数,
		// _this保存原有this
		let args = arguments, _this = this;
		// 如果是第一次执行
		if (firstTime) {
			// 直接调用函数
			fn.apply(_this, args);
			// 并修改firstTime,结束本次函数调用
			return firstTime = false;
		}
		// 如果当前存在定时器,则直接返回,不能继续运行
		if (timer) {
			return false;
		}
		// 如果不存在定时器,进行到这一步
		timer = setTimeout(function () {
			// 先清除之前的定时器
			clearTimeout(timer);
			// 初始化 timer 变量
			timer = null;
			// 调用函数
			fn.apply(_this, args);
		}, interval || 500)
	}
};

/**
 * @function inInSight 判断当前元素是否在可视区域内
 * @param {HTMLElement} el img图片元素
 * @return {Boolean} 返回布尔值,true表示处在可视区域内
 * 
*/
function inInSight(el) {
	let info = el.getBoundingClientRect(); 
	let client = window.innerHeight;
	return info.top <= client - 200;
}

let imgs = document.querySelectorAll('img');
let imgArr = Array.prototype.slice.call(imgs);

// 本文由郝晨光总结并编写,未经允许禁止转载。

/**
 * @function imgLoad 加载图片 通过遍历判断页面上所有的图片,将处在视野内的图片加载到页面上
*/
function imgLoad() {
	// 遍历未加载的图片
	for (let index = 0; index < imgArr.length; index++) {
		// 判断当前图片是否处在视野内
		if (inInSight(imgArr[index])) {
			// 将处在视野内的图片的data-src赋予src属性,则开始加载该图片
			imgArr[index].src = imgArr[index].dataset.src;
			// 在数组内清除该图片
			imgArr.splice(index, 1);
			// index--,继续遍历
			index--;
		}
	}
	// 当图片全部加载完成时,取消监听事件
	if (imgArr.length === 0) {
		window.removeEventListener('scroll', throttleImgLoad);
	}
}

// 节流imgLoad方法,200毫秒触发一次
const throttleImgLoad = throttle(imgLoad, 200);
// 页面初始化调用一次
imgLoad();
// 监听scroll事件,触发懒加载函数
window.addEventListener('scroll', throttleImgLoad);
实现效果

为了看得更清楚一些,我在图片的位置加了一个边框,并且给图片设置了固定的宽高。
图片懒加载

Javascript实现(第二种方式)

写在前边:在第二种方式中,我们使用了 IntersectionObserver 这个API,IntersectionObserver可以自动观察元素是否在视口内。

var io = new IntersectionObserver(callback, option);

// 开始观察
io.observe(document.getElementById('example'));

// 停止观察
io.unobserve(element);

// 关闭观察器
io.disconnect();

callback回调函数,参数是一个数组,每个数组都是一个 IntersectionObserverEntry对象,包括以下属性:

属性描述
time可见性发生变化的时间,单位为毫秒
rootBounds与getBoundingClientRect()方法的返回值一样
boundingClientRect目标元素的矩形区域的信息
intersectionRect目标元素与视口(或根元素)的交叉区域的信息
intersectionRatio目标元素的可见比例,即intersectionRect占boundingClientRect的比例,完全可见时为1,完全不可见时小于等于0
target被观察的目标元素,是一个 DOM 节点对象

我们需要用到 intersectionRatio来判断是否在可视区域内,当intersectionRatio>0&&intersectionRatio<=1即在可视区域内。然后通过target拿到DOM节点,并给它设置src。

let imgs = document.querySelectorAll('img');
let imgArr = Array.prototype.slice.call(imgs);

const io = new IntersectionObserver(ioes => {
	ioes.forEach(ioe => {
		const el = ioe.target;
		const intersectionRatio = ioe.intersectionRatio;
		if(intersectionRatio > 0 && intersectionRatio <= 1) {
			el.src = el.dataset.src;
		}
		el.onload = el.onerror = () => io.unobserve(el);
	})
});

for(let i=0;i<imgArr.length;i++) {
	io.observe(imgArr[i])
}

// 本文由郝晨光总结并编写,未经允许禁止转载。
实现效果

由于这种方法实现的效果过于流畅,所以我换一种方式让大家看这个懒加载的效果,请看右边的Network
图片懒加载



如果本文对您有帮助,可以看看本人的其他文章:
Koa - Node.js框架学习@郝晨光
前端常见面试题(十二)@郝晨光
前端常见面试题(十一)@郝晨光

结言
感谢您的查阅,本文由郝晨光整理并总结,代码冗余或者有错误的地方望不吝赐教;菜鸟一枚,请多关照
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值