图片懒加载

懒加载是一种网页性能优化的方式,它能极大的提升用户体验。图片一直是影响网页性能的主要元凶,现在一张图片超过几兆已经是很经常的事了。如果每次进入页面就请求所有的图片资源,那么可能等图片加载出来用户也早就走了。所以进入页面的时候,只请求可视区域的图片资源。

总结出来就是:

  • 减少资源的加载,页面启动只加载首屏的图片,这样能明显减少了服务器的压力和流量,也能够减小浏览器的负担。
  • 防止并发加载的资源过多而阻塞 js 的加载,影响整个网站的启动,影响用户体验
  • 浪费用户的流量,有些用户并不想全部看完,全部加载会耗费大量流量。

原理

图片懒加载的原理就是暂时不设置图片的 src 属性,而是将图片的 url 隐藏起来,比如先写在 src 里面,等当前图片是否到了可视区域再将图片真实的 url 放进 src 属性里面,从而实现图片的延迟加载。

通过监听 scroll 事件实现懒加载

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function lazyload() {
let viewHeight = document.body.clientHeight //获取可视区高度
let imgs = document.querySelectorAll('img[src]')
imgs.forEach((item, index) => {
if (item.dataset.src === '') return

// 用于获得页面中某个元素的左,上,右和下分别相对浏览器视窗的位置
let rect = item.getBoundingClientRect()
if (rect.bottom >= 0 && rect.top < viewHeight) {
item.src = item.dataset.src
item.removeAttribute('src')
}
})
}

// 可以使用节流优化一下
window.addEventListener('scroll', lazyload)

通过上面例子的实现,我们要实现懒加载都需要去监听 scroll 事件,尽管我们可以通过函数节流的方式来阻止高频率的执行函数,但是我们还是需要去计算 scrollTop,offsetHeight 等属性,有没有简单的不需要计算这些属性的方式呢,答案是有的—-IntersectionObserver

IntersectionObserver实现懒加载

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
const imgs = document.querySelectorAll('img[src]')
const config = {
rootMargin: '0px',
threshold: 0,
}
let observer = new IntersectionObserver((entries, self) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
let img = entry.target
let src = img.dataset.src
if (src) {
img.src = src
img.removeAttribute('src')
}
// 解除观察
self.unobserve(entry.target)
}
})
}, config)

imgs.forEach((image) => {
observer.observe(image)
})

vue中使用IntersectionObserver实现图片懒加载指令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
Vue.directive('img-lazy', {
inserted(el, binding) {
// 观察当前元素
el.src = require('~/assets/image/public/placeholder-graphic.png'); // 默认图片(占位图)
const defaultImg = require('~/assets/image/public/load-error-img.png'); // 图片加载失败图片
const observer = new IntersectionObserver(([{ isIntersecting }]) => {
if (isIntersecting) {
// 停止观察
observer.unobserve(el);
// 若图片加载失败,使用该默认图片
el.onerror = () => {
el.src = defaultImg;
};
// 组件使用指令传来的值进行操作,赋值于src
el.src = binding.value;
}
}, {
// 进入区域立即观察
threshold: 0
});
// 挂载元素,只进行一次观察:开始观察
observer.observe(el);
}
})

vue中调用

1
<img v-img-lazy="src" alt="">