1、什么是懒加载?
懒加载就是延迟加载的意思,比如我们加载一个页面,这个页面很长很长,长到我们的浏览器可视区域装不下,那么懒加载就是优先加载可视区域的内容,其他部分等进入了可视区域在加载。
2、为什么要懒加载?
懒加载是一种网页性能优化的方式,它能极大的提升用户体验。就比如说图片,图片一直是影响网页性能的主要元凶,现在一张图片超过几兆已经是很经常的事了。如果每次进入页面就请求所有的图片资源,那么可能等图片加载出来用户也早就走了。所以,我们需要懒加载,进入页面的时候,只请求可视区域的图片资源。
总结两个点:
- 全部加载的话会影响用户体验
- 浪费用户的流量,有些用户并不像全部看完,全部加载会耗费大量流量。
3、懒加载实现原理?
原理:大家都知道,一张图片就是一个 <img>
标签,而图片的来源主要是 src 属性。浏览器是否发起亲求就是根据是否有 src 属性决定的。
既然这样,那么我们就要对 <img />
标签的 src 属性下手了,在没进入可视区域的时候,我们先不给这个 <img />
标签赋 src 属性,这样岂不是浏览器就不会发送请求了。
我们知道了从 <img>
标签下手,那么更重要的就是可视区域的判断了。
4、与懒加载有关的 API
① 获取屏幕可视区的高度
document.documentElement.clientHeight
MDN:这个属性是只读属性,对于没有定义 CSS 或者内联布局盒子的元素为 0,否则,它是元素内部的高度(单位像素),包含内边距,但不包括滚动条、边框和外边距。
② 获取元素相对于文档顶部的高度
element.offsetTop
MDN:为只读属性,它返回当前元素相对其 offsetParent
元素的顶部距离。
③ 获取浏览器窗口顶部与文档顶部之间的距离,也就是滚动条滚动的距离
document.documentElement.scrollTop
MDN:属性可以获取或设置一个元素的内容垂直滚动的像素数。当一个元素的内容没有产生垂直方向的滚动条,那么它的 scrollTop 值为 0。
④ 通过上面三个API,我们获得了三个值:可视区高度、元素相对于其父元素容器顶部的距离、浏览器窗口顶部与容器元素顶部的距离也就是滚动条滚动的高度。
⑥ 如果:offsetTop - scrollTop < clientHeight 则图片进入可视区内,则被请求。
五、代码实现
① 第一种实现方式
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
img {
display: block;
width: 100%;
height: 300px;
margin-bottom: 20px;
}
</style>
</head>
<body>
<div class="container">
<img data-src="https://dss0.bdstatic.com/70cFuHSh_Q1YnxGkpoWK1HF6hhy/it/u=1819216937,2118754409&fm=26&gp=0.jpg">
<img data-src="https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=2654365388,3749485282&fm=26&gp=0.jpg">
<img data-src="https://ns-strategy.cdn.bcebos.com/ns-strategy/upload/fc_big_pic/part-00487-1155.jpg">
<img data-src="https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=239110925,962980089&fm=26&gp=0.jpg">
<img data-src="https://ss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/u=1808664313,530970200&fm=26&gp=0.jpg">
<img data-src="https://ss1.bdstatic.com/70cFvXSh_Q1YnxGkpoWK1HF6hhy/it/u=3325605116,3025793151&fm=26&gp=0.jpg">
<img data-src="https://ss0.bdstatic.com/70cFuHSh_Q1YnxGkpoWK1HF6hhy/it/u=1100786670,1609279816&fm=26&gp=0.jpg">
<img data-src="https://ss1.bdstatic.com/70cFvXSh_Q1YnxGkpoWK1HF6hhy/it/u=2665728391,1876846198&fm=26&gp=0.jpg">
<img data-src="https://ss1.bdstatic.com/70cFvXSh_Q1YnxGkpoWK1HF6hhy/it/u=1883571456,3298240469&fm=26&gp=0.jpg">
<img data-src="https://ss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=3065485140,2677122975&fm=26&gp=0.jpg">
<img data-src="https://ss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=2685177128,4134246955&fm=26&gp=0.jpg">
</div>
<script>
var imgs = document.querySelectorAll("img");
// 获取元素到顶部的距离
function getPageOffset(el){
var left = el.offsetLeft;
var top = el.offsetTop;
while(el.offsetParent){
el = el.offsetParent;
left += el.offsetLeft + el.clientLeft;
top += el.offsetTop + el.clientTop;
}
return { left, top };
}
// 图片懒加载
function lazyLoad(imgs) {
var clientH = document.documentElement.clientHeight;
var scrollT = document.documentElement.scrollTop || document.body.scrollTop;
for (let i = 0; i < imgs.length; i++) {
var { top } = getPageOffset(imgs[i]);
if (clientH + scrollT > top) {
imgs[i].src = imgs[i].getAttribute("data-src");
}
}
}
window.onload = window.onscroll = function() {
lazyLoad(imgs);
}
</script>
</body>
</html>
② 第二种实现方式
利用 getBoundingClientRect()
来获取元素的大小及位置。
MDN:该方法返回元素的大小及其相对于是视口的位置。
实现方式:
懒加载的一个重点就是要知道什么时候图片是进入了可视区内,那么就上面这张图而言,我们有什么方法判断图片进入了可视区呢。
其实很简单:我们先获取图片到可视区顶部的距离,并获取到可视区的高度:
var bound = el.getBoundingClientRect();
var clientHeight = window.innerHeight;//这个和前面获取可视区高度的效果一样,只是兼容性问题
当我们滚动条向下滚动的时候,bound.top
值会变得越来越小,也就是图片到可视区顶部的距离也越来越小,当 bound.top == clientHeight
时,说明土片马上就要进入可视区了,只要我们在滚动,图片就会进入可视区,那么就需要请求资源了。也就是说,在 bound.top<=clientHeight
时,图片是在可视区域内的。
代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
img {
display: block;
width: 100%;
height: 300px;
margin-bottom: 20px;
}
</style>
</head>
<body>
<div class="container">
<img data-src="https://dss0.bdstatic.com/70cFuHSh_Q1YnxGkpoWK1HF6hhy/it/u=1819216937,2118754409&fm=26&gp=0.jpg">
<img data-src="https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=2654365388,3749485282&fm=26&gp=0.jpg">
<img data-src="https://ns-strategy.cdn.bcebos.com/ns-strategy/upload/fc_big_pic/part-00487-1155.jpg">
<img data-src="https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=239110925,962980089&fm=26&gp=0.jpg">
<img data-src="https://ss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/u=1808664313,530970200&fm=26&gp=0.jpg">
<img data-src="https://ss1.bdstatic.com/70cFvXSh_Q1YnxGkpoWK1HF6hhy/it/u=3325605116,3025793151&fm=26&gp=0.jpg">
<img data-src="https://ss0.bdstatic.com/70cFuHSh_Q1YnxGkpoWK1HF6hhy/it/u=1100786670,1609279816&fm=26&gp=0.jpg">
<img data-src="https://ss1.bdstatic.com/70cFvXSh_Q1YnxGkpoWK1HF6hhy/it/u=2665728391,1876846198&fm=26&gp=0.jpg">
<img data-src="https://ss1.bdstatic.com/70cFvXSh_Q1YnxGkpoWK1HF6hhy/it/u=1883571456,3298240469&fm=26&gp=0.jpg">
<img data-src="https://ss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=3065485140,2677122975&fm=26&gp=0.jpg">
<img data-src="https://ss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=2685177128,4134246955&fm=26&gp=0.jpg">
</div>
<script>
var imgs = document.querySelectorAll("img");
function isIn(img) {
var top = img.getBoundingClientRect().top;
var innerH = window.innerHeight;
return top <= innerH;
}
function check(imgs) {
[...imgs].forEach(img => {
if (isIn(img)) {
lazyLoad(img);
}
})
}
function lazyLoad(img) {
if (!img.src) {
img.src = img.dataset.src;
}
}
window.onload = window.onscroll = function() {
check(imgs);
}
</script>
</body>
</html>