参考答案:
懒加载是一种网页性能优化的方式,它能极大的提升用户体验。就比如说图片,图片一直是影响网页性能的主要元凶,现在一张图片超过几兆已经是很经常的事了。如果每次进入页面就请求所有的图片资源,那么可能等图片加载出来用户也早就走了。所以,我们需要懒加载,进入页面的时候,只请求可视区域的图片资源。
总结出来就两个点:
最简单的实现方式是给 img
标签加上 loading="lazy"
,比如
1<img src="./example.jpg" loading="lazy">
该属性的兼容性也还行,大家生产环境可以使用。
我们通过js监听页面的滚动也能实现。
使用js实现的原理主要是判断当前图片是否到了可视区域:
在页面初始化的时候,<img>图片的src实际上是放在data-src属性上的,当元素处于可视范围内的时候,就把data-src赋值给src属性,完成图片加载。
<div>使用背景图来实现,原理也是一样的,把图片链接存放在 `data-src` 中,在可视范围时,就把data-src赋值给 `background-image` 属性,完成图片加载。1// 在一开始加载的时候 2<img data-src="http://xx.com/xx.png" src="" /> 3 4// 在进入可视范围内时 5<img data-src="http://xx.com/xx.png" src="http://xx.com/xx.png" />
1// 在一开始加载的时候 2<div 3 data-src="http://xx.com/xx.png" 4 style="background-image: none;background-size: cover;" 5></div> 6 7// 在进入可视范围内时 8<div 9 data-src="http://xx.com/xx.png" 10 style="background-image: url(http://xx.com/xx.png);background-size: cover;" 11></div>
下面展示一个demo:
1<html lang="en"> 2 <head> 3 <meta charset="UTF-8" /> 4 <title>Lazyload</title> 5 <style> 6 img { 7 display: block; 8 margin-bottom: 50px; 9 height: 200px; 10 width: 400px; 11 } 12 </style> 13 </head> 14 <body> 15 <img src="./img/default.png" data-src="./img/1.jpg" /> 16 <img src="./img/default.png" data-src="./img/2.jpg" /> 17 <img src="./img/default.png" data-src="./img/3.jpg" /> 18 <img src="./img/default.png" data-src="./img/4.jpg" /> 19 <img src="./img/default.png" data-src="./img/5.jpg" /> 20 <img src="./img/default.png" data-src="./img/6.jpg" /> 21 <img src="./img/default.png" data-src="./img/7.jpg" /> 22 <img src="./img/default.png" data-src="./img/8.jpg" /> 23 <img src="./img/default.png" data-src="./img/9.jpg" /> 24 <img src="./img/default.png" data-src="./img/10.jpg" /> 25 </body> 26</html>
先获取所有图片的 dom,通过 window.innerHeight || document.documentElement.clientHeight|| document.body.clientHeight
获取可视区高度,再使用 element.getBoundingClientRect()
API 直接得到元素相对浏览的 top 值, 遍历每个图片判断当前图片是否到了可视区范围内。代码如下:
1function lazyload() { 2 let viewHeight = window.innerHeight || document.documentElement.clientHeight|| document.body.clientHeight //获取可视区高度,兼容不同浏览器 3 let imgs = document.querySelectorAll('img[data-src]') 4 imgs.forEach((item, index) => { 5 if (item.dataset.src === '') return 6 7 // 用于获得页面中某个元素的左,上,右和下分别相对浏览器视窗的位置 8 let rect = item.getBoundingClientRect() 9 if (rect.bottom >= 0 && rect.top < viewHeight) { 10 item.src = item.dataset.src 11 item.removeAttribute('data-src') 12 } 13 }) 14}
最后给 window 绑定 onscroll 事件
1window.addEventListener('scroll', lazyload)
主要就完成了一个图片懒加载的操作了。但是这样存在较大的性能问题,因为 scroll 事件会在很短的时间内触发很多次,严重影响页面性能,为了提高网页性能,我们需要一个节流函数来控制函数的多次触发,在一段时间内(如 200ms)只执行一次回调。
下面实现一个节流函数
1function throttle(fn, delay) { 2 let timer 3 let prevTime 4 return function (...args) { 5 const currTime = Date.now() 6 const context = this 7 if (!prevTime) prevTime = currTime 8 clearTimeout(timer) 9 10 if (currTime - prevTime > delay) { 11 prevTime = currTime 12 fn.apply(context, args) 13 clearTimeout(timer) 14 return 15 } 16 17 timer = setTimeout(function () { 18 prevTime = Date.now() 19 timer = null 20 fn.apply(context, args) 21 }, delay) 22 } 23}
然后修改一下 srcoll 事件
1window.addEventListener('scroll', throttle(lazyload, 200))
通过上面例子的实现,我们要实现懒加载都需要去监听 scroll 事件,尽管我们可以通过函数节流的方式来阻止高频率的执行函数,但是我们还是需要去计算 scrollTop,offsetHeight 等属性,有没有简单的不需要计算这些属性的方式呢,答案就是 IntersectionObserver
。
IntersectionObserver
是一个比较新的 API,可以自动"观察"元素是否可见,Chrome 51+ 已经支持。由于可见(visible)的本质是,目标元素与视口产生一个交叉区,所以这个 API 叫做"交叉观察器"。我们来看一下它的用法:
1var io = new IntersectionObserver(callback, option) 2 3// 开始观察 4io.observe(document.getElementById('example')) 5 6// 停止观察 7io.unobserve(element) 8 9// 关闭观察器 10io.disconnect() 11
IntersectionObserver 是浏览器原生提供的构造函数,接受两个参数:callback 是可见性变化时的回调函数,option 是配置对象(该参数可选)。
目标元素的可见性变化时,就会调用观察器的回调函数 callback。callback 一般会触发两次。一次是目标元素刚刚进入视口(开始可见),另一次是完全离开视口(开始不可见)。
下面我们用 IntersectionObserver 实现图片懒加载
1const imgs = document.querySelectorAll('img[data-src]') 2const config = { 3 rootMargin: '0px', 4 threshold: 0, 5} 6let observer = new IntersectionObserver((entries, self) => { 7 entries.forEach((entry) => { 8 if (entry.isIntersecting) { 9 let img = entry.target 10 let src = img.dataset.src 11 if (src) { 12 img.src = src 13 img.removeAttribute('data-src') 14 } 15 // 解除观察 16 self.unobserve(entry.target) 17 } 18 }) 19}, config) 20 21imgs.forEach((image) => { 22 observer.observe(image) 23}) 24
最近更新时间:2024-08-10