图片懒加载原理及实现(java懒加载原理)

一,前置知识1,为什么要图片懒加载懒加载是一种对网页性能优化的方式,比如当访问一个页面的时候,优先显示可视区域的图片而不是一次性加载所有图片,当需要显示时,再发送图片请求,避免打开网页时加载过多资源。当一个网站的加载图片过多时就需要懒加载的协助,页面图片多时,在首次载入时一次性加载会耗费时间长,使用懒加载可以使页面加载速度快、减轻服务器的压力、节省流量。如下图:随着滚轮滚动,底部的图片会被不断地加载,从而显示在页面上,也就是说懒加载其实就是按需加载,当页面需要显示图片的时候才进行加载,否则不加载

大家好,又见面了,我是你们的朋友全栈君。

一,前置知识

1,为什么要图片懒加载

懒加载是一种对网页性能优化的方式,比如当访问一个页面的时候,优先显示可视区域的图片而不是一次性加载所有图片,当需要显示时,再发送图片请求,避免打开网页时加载过多资源。
当一个网站的加载图片过多时就需要懒加载的协助,页面图片多时,在首次载入时一次性加载会耗费时间长,使用懒加载可以使页面加载速度快、减轻服务器的压力、节省流量。
如下图:
在这里插入图片描述
随着滚轮滚动,底部的图片会被不断地加载,从而显示在页面上,也就是说懒加载其实就是按需加载,当页面需要显示图片的时候才进行加载,否则不加载。
那问题来了,直接发起http请求,下载所有图片,然后存储在本地,再进行页面渲染不行吗???
这里就要讲图片的特殊性了,常规情况下,我们图片是这样写的:

<img src="https://img.pic88.com/preview/2020/08/01/1596324521812272.jpg!s640">

它用的是src来链接外部资源,用来替换这个位置。它是可以无视跨域的(基于这一点,才有了jsonp的跨域实现)。
也就是说,它不是页面加载时发起http请求获取页面数据时直接得到的图片,而是先得到:
在这里插入图片描述
也就是说只是得到这么一个记录图片位置的字符串,然后把数据赋值给:

<img src="https://img.pic88.com/preview/2020/08/01/1596324521812272.jpg!s640">

然后让image的src来发起请求,获取对应的图片放置到DOM树的这个位置上,从而实现图片的页面渲染!
于是就可以知道,当进入页面的时候,其实我们已经把所有的图片的这个地址信息拿到了,图片懒加载的作用就是让这个图片的src按需发起请求,获取图片。
同样是这个界面,当我滚动滚轮时,看network的请求情况:
在这里插入图片描述
随着滚轮的滚动,不断地发起请求来获取图片。
接着查看这个瀑布图:
在这里插入图片描述
可以看到每次只加载2张图片(这是因为我页面每行只有2张图片,按照它懒加载的算法,每次都是两张图片同时进入屏幕,所以才同时获取两张图片),这样就把请求错开了。
但是如果不用图片懒加载的话,就会是这个样子的:
在这里插入图片描述
因为浏览器可以并行加载图片(不超过10个并行任务好像……额,是好像),所以就好多图片一起加载了 ,这还只是8张图片,那如果是大量的图片呢?那不就是把主线程大量占用了嘛!
所以我们需要使用图片的懒加载技术来优化页面,最大的目的就是让主线程空闲变多,页面加载更快。

2,实现图片懒加载的原理

看到这里,我们已经明白了图片加载是src属性做的事情,那么我们只要不给这个src属性赋值不就不会发起请求了嘛!!
然后,html5还提供自定义属性的方式:data-xxx
我们可以先把图片地址存在这里面,然后判断这个图片是否进入屏幕了,一旦进入屏幕,就把这个图片地址赋值给src,让它发起请求获取图片不就好了!

<div class="box">
			<img src=""  alt="1">
			<img src=""  alt="2">
			<img src=""  alt="3">
			<img src=""  alt="4">
			<img src=""  alt="5">
			<img src=""  alt="6">
			<img src=""  alt="7">
			<img src=""  alt="8">
		</div>
<img data-src="./图片懒加载/1.jpg" alt="">

但是实际上,我们的网络也没有那么稳定,如果网络不佳,图片将会是这样的:
在这里插入图片描述
这将会造成页面布局的混乱,为了避免这种情况,前端的小伙伴们第一时间肯定是想到占位。那通过啥占位?通常是一张loading的gif动图,这样可以给用户带来更好的体验,但是新的问题又出现了!占位的图片它既然也是通过src取得的,那它占那么多位置,不也需要发起请求嘛???
实际上,浏览器已经很聪明了,当页面的有多张重复图片使用时,它只会发起一次http请求,后续就直接使用了,不会再发请求,也就是说,使用占位图片,只是额外增加一个图片请求罢了。。大气如本少年,肯定是允许的呀!!

3,实现判断图片是否在屏幕上

最基础的,主要是需要使用到这两个方法:

1,DOMobj.getBoundingClientRect().top   //获取该元素到屏幕顶部的距离
2,window.innerHeight    //屏幕的高度

也就是当元素到屏幕顶部的距离小于屏幕高度时,就可以判定图片进入了屏幕。就把图片地址赋值给src,从而发起请求获取图片。
在这里插入图片描述

二,原生实现图片的懒加载

基于上面说的方法,写的原生实现代码:

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
		<style type="text/css">
			.lazy-load{ 
   
				display: block;
				width: 90%;
				margin: 0 auto;
			}
		</style>
	</head>
	<body>
		<div class="box">
			<img data-src="./image/1.jpg"  alt="1" class="lazy-load" src="https://img.pic88.com/preview/2020/08/01/1596324521812272.jpg!s640">
			<img data-src="./image/2.jpg"  alt="2" class="lazy-load" src="https://img.pic88.com/preview/2020/08/01/1596324521812272.jpg!s640">
			<img data-src="./image/3.jpg"  alt="3" class="lazy-load" src="https://img.pic88.com/preview/2020/08/01/1596324521812272.jpg!s640">
			<img data-src="./image/4.jpg"  alt="4" class="lazy-load" src="https://img.pic88.com/preview/2020/08/01/1596324521812272.jpg!s640">
			<img data-src="./image/5.jpg"  alt="5" class="lazy-load" src="https://img.pic88.com/preview/2020/08/01/1596324521812272.jpg!s640">
			<img data-src="./image/6.jpg"  alt="6" class="lazy-load" src="https://img.pic88.com/preview/2020/08/01/1596324521812272.jpg!s640">
			<img data-src="./image/7.jpg"  alt="7" class="lazy-load" src="https://img.pic88.com/preview/2020/08/01/1596324521812272.jpg!s640">
			<img data-src="./image/8.jpg"  alt="8" class="lazy-load" src="https://img.pic88.com/preview/2020/08/01/1596324521812272.jpg!s640">
		</div>
	</body>

	<script type="text/javascript">
		//图片加载的函数
		function imgonload() { 
   
			//把伪数组转化为真数组
		    let imgs = [...document.querySelectorAll('.lazy-load')]
		    for(let i=0; i<imgs.length; i++) { 
   
		      if(imgs[i].getBoundingClientRect().top < window.innerHeight) { 
   
		        //图片一旦有src就会加载出来,所以图片的路径不会放在src中,而是一个自定义的属性data-src中
		        imgs[i].src = imgs[i].dataset.src;
		      }
		    }
		  }
		  //第一次页面加载的时候,让屏幕内的图片正常加载,而屏幕外的图片就使用loading图片代替
		 window.onload = imgonload;
		 //监听滚轮事件,
		 window.addEventListener('scroll',imgonload)
		
	</script>
</html>

实现的效果:
在这里插入图片描述
值得注意的是,监听鼠标滚轮的滚动太频繁了,可以进行节流优化,让固定500ms执行一次:

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
		<style type="text/css">
			.lazy-load{ 
   
				display: block;
				width: 90%;
				margin: 0 auto;
			}
		</style>
	</head>
	<body>
		<div class="box">
			<img data-src="./image/1.jpg"  alt="1" class="lazy-load" src="https://img.pic88.com/preview/2020/08/01/1596324521812272.jpg!s640">
			<img data-src="./image/2.jpg"  alt="2" class="lazy-load" src="https://img.pic88.com/preview/2020/08/01/1596324521812272.jpg!s640">
			<img data-src="./image/3.jpg"  alt="3" class="lazy-load" src="https://img.pic88.com/preview/2020/08/01/1596324521812272.jpg!s640">
			<img data-src="./image/4.jpg"  alt="4" class="lazy-load" src="https://img.pic88.com/preview/2020/08/01/1596324521812272.jpg!s640">
			<img data-src="./image/5.jpg"  alt="5" class="lazy-load" src="https://img.pic88.com/preview/2020/08/01/1596324521812272.jpg!s640">
			<img data-src="./image/6.jpg"  alt="6" class="lazy-load" src="https://img.pic88.com/preview/2020/08/01/1596324521812272.jpg!s640">
			<img data-src="./image/7.jpg"  alt="7" class="lazy-load" src="https://img.pic88.com/preview/2020/08/01/1596324521812272.jpg!s640">
			<img data-src="./image/8.jpg"  alt="8" class="lazy-load" src="https://img.pic88.com/preview/2020/08/01/1596324521812272.jpg!s640">
		</div>
	</body>
	
	<script type="text/javascript">
		//图片加载的函数
		function imgonload() { 
   
			//把伪数组转化为真数组
		    let imgs = [...document.querySelectorAll('.lazy-load')]
		    for(let i=0; i<imgs.length; i++) { 
   
		      if(imgs[i].getBoundingClientRect().top < window.innerHeight) { 
   
		        //图片一旦有src就会加载出来,所以图片的路径不会放在src中,而是一个自定义的属性data-src中
		        imgs[i].src = imgs[i].dataset.src;
		      }
		    }
		  }
		  //第一次页面加载的时候,让屏幕内的图片正常加载,而屏幕外的图片就使用loading图片代替
		 window.onload = imgonload;
		 //监听滚轮事件,
		 let beAbleClick = true;
		//设置节流,让500ms内只执行一次这个函数
		window.addEventListener('scroll', () => { 
   
		//在设定的时间内,则不执行函数
		  if (!beAbleClick) { 
   
			return;
		  }
		  //在设定的时间外,则开始计时,过500ms后执行
		  setTimeout(() => { 
   
			imgonload();
			//事件执行完成之后,才设置为true,才可以进行下一次事件的是触发
			beAbleClick = true;
		  }, 500);
		  //设定的时间内将状态设为false,事件循环机制,开始计时期间,他执行变成false
		  beAbleClick = false;
		});
	</script>
</html>

节流之后,可以明显看到,没过500ms才判断一次图片是否在屏幕中,节流之后,又让主线程空闲优化了一些;当然,实际不会设500ms这么大的,这里只是让效果更明显一些。
在这里插入图片描述

三,使用:Intersection Observer API

Intersection Observer API提供了一种异步检测目标元素与祖先元素或 viewport 相交情况变化的方法。
过去,要检测一个元素是否可见或者两个元素是否相交并不容易,很多解决办法不可靠或性能很差。然而,随着互联网的发展,这种需求却与日俱增,比如,下面这些情况都需要用到相交检测:

1,图片懒加载——当图片滚动到可见时才进行加载
2,内容无限滚动——也就是用户滚动到接近内容底部时直接加载更多,而无需用户操作翻页,给用户一种网页可以无限滚动的错觉
3,检测广告的曝光情况——为了计算广告收益,需要知道广告元素的曝光情况
4,在用户看见某个区域时执行任务或播放动画
5,过去,相交检测通常要用到事件监听,并且需要频繁调用Element.getBoundingClientRect()方法以获取相关元素的边界信息。事件监听和调用Element.getBoundingClientRect()都是在主线程上运行,因此频繁触发、调用可能会造成性能问题。这种检测方法极其怪异且不优雅。
<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
		<style type="text/css">
			.lazy-load{ 
   
				display: block;
				width: 90%;
				margin: 0 auto;
			}
		</style>
	</head>
	<body>
		<div class="box">
			<img data-src="./image/1.jpg"  alt="1" class="lazy-load" src="https://img.pic88.com/preview/2020/08/01/1596324521812272.jpg!s640">
			<img data-src="./image/2.jpg"  alt="2" class="lazy-load" src="https://img.pic88.com/preview/2020/08/01/1596324521812272.jpg!s640">
			<img data-src="./image/3.jpg"  alt="3" class="lazy-load" src="https://img.pic88.com/preview/2020/08/01/1596324521812272.jpg!s640">
			<img data-src="./image/4.jpg"  alt="4" class="lazy-load" src="https://img.pic88.com/preview/2020/08/01/1596324521812272.jpg!s640">
			<img data-src="./image/5.jpg"  alt="5" class="lazy-load" src="https://img.pic88.com/preview/2020/08/01/1596324521812272.jpg!s640">
			<img data-src="./image/6.jpg"  alt="6" class="lazy-load" src="https://img.pic88.com/preview/2020/08/01/1596324521812272.jpg!s640">
			<img data-src="./image/7.jpg"  alt="7" class="lazy-load" src="https://img.pic88.com/preview/2020/08/01/1596324521812272.jpg!s640">
			<img data-src="./image/8.jpg"  alt="8" class="lazy-load" src="https://img.pic88.com/preview/2020/08/01/1596324521812272.jpg!s640">
		</div>
	</body>
	
	<script type="text/javascript">
		document.addEventListener("DOMContentLoaded", function() { 
   
		    let lazyImages = [...document.querySelectorAll('.lazy-load')];
		    if ("IntersectionObserver" in window) { 
   
		        // 创建一个观察函数,以便待会调用 
		        let lazyImageObserver = new IntersectionObserver(function(entries, observer) { 
   
		            entries.forEach(function(entry) { 
   
		            if (entry.isIntersecting) { 
   
		               let lazyImage = entry.target;
		               lazyImage.src = lazyImage.dataset.src;   // 替换 src URL
		               lazyImageObserver.unobserve(lazyImage);  // 解除观察
		            }
		            });
		        });
		        // 对所有需要懒加载的图片进行 “暗中观察”
		        lazyImages.forEach(function(lazyImage) { 
   
		            lazyImageObserver.observe(lazyImage);
		        });
		     }else{ 
   
		           alert('您的浏览器不支持 IntersectionObserver');
		     }
		});

	</script>
</html>

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/127400.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • pycharm基本操作_有哪些简单的电脑运行

    pycharm基本操作_有哪些简单的电脑运行很多初学者一上来就开始使用Pycharm写代码,我个人不太建议这么做,因为IDE强大的功能背后隐藏了很多技术细节,以至于脱离了IDE就不会写代码了,所以在初级阶段还是老老实实用一些简单的编辑器来写,比如sublime或者Vim,这样有利用加深对Python标准库中常用模块和方法的记忆,也有易于对问题的定位和排查。当熟悉一门语言后,我们就应该找到更高效的工具来提高生产效率,Pycharm是

  • 免费SSL证书(https网站)申请

    免费SSL证书(https网站)申请如何拥有一个自己的免费的SSL证书,并且能够长期拥有。这篇文章让你找到可用的免费证书o(* ̄︶ ̄*)o各厂商提供的免费SSL基本是Symantec(赛门铁克),申请一年,不支持通配符,有数量限制。

  • JDBC_3 数据库事物

    JDBC_3 数据库事物数据库事务数据一旦提交,就不可回滚那些操作会导致数据的自动提交?DDL操作一旦执行,都会自动提交-. set autocommit = false不起作用DML默认情况下,一旦执行就会自动提交-. 可以设置set autocommit = false关闭连接的时候会自动提交 Connection connection = DriverManager.getConnection(url, user, password); connection.setAutoCommit

  • 775针cpu性能最好的_英特尔775针cpu性能排行

    775针cpu性能最好的_英特尔775针cpu性能排行排名型号评分1IntelCorei7995X@3.60GHz10,8622IntelXeonW3690@3.47GHz10,8283IntelCorei7990X@3.47GHz10,6544IntelCorei7980X@3.33GHz10,6075IntelXeonX5690@3.47GHz10,3146IntelCorei7980@3….

  • web安全常见漏洞_web漏洞挖掘

    web安全常见漏洞_web漏洞挖掘常见Web漏洞小结1越权漏洞不同权限账户之间的存在越权访问检测抓去a用户功能链接,然后登录b用户对此链接进行访问抓去a用户功能链接,修改id为b的id,查看是否能看b的相关数据替换不同的cookie进行测试查看防范1服务器端必须对每个页面链接进行权限判断。2用户登陆后,服务器端不应再以客户端提交的用户身份信息为依据,而应以会话中服务端保存的已登陆的用户身份信息为准。3页面提交的资源标志与已登陆的用户身份进行匹配比对,然后判断其对当前链接是否有权限。4必须在服务器端对每个请求URL进行鉴

  • 惠普电脑指纹锁_利用计算机对指纹进行识别

    惠普电脑指纹锁_利用计算机对指纹进行识别按下电源键,输入密码,咦?密码输错了……再输……咦?又错了!开机密码是啥来着?设置开机密码很普遍(图片引自网络)相信很多朋友都有过上述经历。为了让笔记本更安全,于是设置了一个开机密码。并且为了提高密码的安全性,时常需要过段时间就更新一次,所以经常一着急就给忘了。设置密码虽好,但是它也有两点弊端,一是增加了开机时间,二是存在遗忘和被盗的风险。想想看,你每次开机是不是都要输入一组6位左右的数字或者字母…

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号