图片懒加载

什么是懒加载

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

什么时候使用懒加载

  1. 当页面中一次性载入很多图片的时候
  2. SPA页面中的图片一定要做懒加载,否则你会发现路由跳转很慢,很慢。因为新页面路由是在dom挂在完之后才会跳转的。如果你的页面有很多图片,那么一定要做图片懒加载,否则会瞬间发出很多请求,还涉及页面渲染,重绘。因为处理并发请求比较慢,浏览器对同一域名并发http请求是有限制的(chrome是6个,一般都在8个以内),但是加载到渲染还是很消耗性能。可以在dom mount之后,加个延时做图片懒加载。这样页面会跳转会快很多。

懒加载原理

标签有一个属性是src,用来表示图像的URL,当这个属性的值不为空时,浏览器就会根据这个值发送请求。如果没有src属性,就不会发送请求。

可以通过先不给 设置 src,把图片真正的 URL 放在另一个属性 data-src 中,在需要的时候也就是图片进入可视区域的之前,将 URL 取出放到 src 中。

实现

HTML结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<div class="container">
<div class="img-area">
<img class="my-photo" alt="loading" data-src="./img/img1.png">
</div>
<div class="img-area">
<img class="my-photo" alt="loading" data-src="./img/img2.png">
</div>
<div class="img-area">
<img class="my-photo" alt="loading" data-src="./img/img3.png">
</div>
<div class="img-area">
<img class="my-photo" alt="loading" data-src="./img/img4.png">
</div>
<div class="img-area">
<img class="my-photo" alt="loading" data-src="./img/img5.png">
</div>
</div>

仔细观察一下, 标签此时是没有 src 属性的,只有 alt 和 data-src 属性。

alt 属性是一个必需的属性,它规定在图像无法显示时的替代文本。 data-* 全局属性:构成一类名称为自定义数据属性的属性,可以通过HTMLElement.dataset来访问。

如何判断元素是否在可视区域

通过scrollTop判断

  1. 通过document.documentElement.clientHeight获取屏幕可视窗口高度
  2. 通过element.offsetTop获取元素相对于文档顶部的距离
  3. 通过document.documentElement.scrollTop获取浏览器窗口顶部与文档顶部之间的距离,也就是滚动条滚动的距离

然后判断element.offsetTop - document.documentElement.scrollTop < document.documentElement.clientHeight是否成立,如果成立,元素就在可视区域内。

使用getBoundingClientRect

通过getBoundingClientRect()方法获取元素的大小及其相对于视口的位置,这个方法返回一个名为ClientRectDOMRect对象,包含了top、right、bottom、left、width、height这些值

返回的元素位置是相对于左上角而言的,而不是边距。假设const bound = el.getBoundingClientRect();来表示图片到可视区域顶部距离; 并设 const clientHeight = window.innerHeight; 来表示可视区域的高度。随着滚动条的向下滚动,bound.top会越来越小,也就是图片到可视区域顶部的距离越来越小,当bound.top===clientHeight时,图片的上沿应该是位于可视区域下沿的位置的临界点,再滚动一点点,图片就会进入可视区域。也就是说,在bound.top<=clientHeight时,图片是在可视区域内的。

1
2
3
4
5
6
7
8
function isInSight(el) {
const bound = el.getBoundingClientRect();
const clientHeight = window.innerHeight;
// 如果只考虑向下滚动加载
// const clientWidth = window.innerWeight;
// +100是为了提前加载。
return bound.top <= clientHeight + 100;
}

加载图片

页面打开时需要对所有图片进行检查,是否在可视区域内,如果是就加载。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function checkImgs() {
const imgs = document.querySelectorAll(".my-photo");
Array.from(imgs).forEach(el => {
if (isInSight(el)) {
loadImg(el);
}
});
}

function loadImg(el) {
// 这里可以保存一个已加载过的图片,这样每次检查的时候就不用循环全部的图片了
if (!el.src) {
const source = el.dataset.src;
el.src = source;
}
}

上面的代码还有个问题就是 checkImgs 函数什么时候执行,这就要用来函数的防抖