十分钟搞懂前端高频考点:图片懒加载
前言
依稀记得,小时候打卡的网页都是图片少,文字多,在那个“龟速网络”的年代,前端确实不太好插一堆图片在网页上。但如今百兆网甚至千兆网都已经普及的情况下,为了更好的用户体验,更多,更大,更清晰的图片被插入到网页之中,但是在特殊情况下(比如网络质量差且用户频繁滚动页面),就会导致下面的图片加载不出来,用户看到的是一片空白。这就需要用到图片懒加载技术,使用户能够得到更好的体验。
正文
什么是懒加载?
相信大家都网购过,当我们在购物网站快速下滑的时候,会发现页面中该出现图片的地方却出现了短暂的空白。这是由于图片需要加载的原因。现在各位和我一起想象一下,假设这个购物网站有三米长,而你的屏幕只有一米长。现在,你一打开网页就直接滑到了网页的最低端。(就好比这样)
而恰好你的网络这个时候卡了,网速很慢,那么你就不得不等前面两米的网页内容里面的所有图片都加载完之后,才能开始加载你所在位置的图片,那得等多久啊?这个时候就想,要是可以我看哪里就加载哪里的图片多好,这样就算我网络卡了,至少也能先加载我看的这块内容的图片。而这就是懒加载技术
手写懒加载
首先我们需要知道的是,之所以会造成卡顿,是因为目前来说绝大部分图片的引入都是src引入的,而src的特点就是会阻塞后面的内容的加载。浏览器是个死脑筋,碰到src就死磕到底,不加载出来我今天就不走了!所以,如果面试官要求我们手写一个懒加载,那最最简单的方式就是从src入手。
先来上一个小demo(内部所用图片均来源于百度,如有侵权请联系删除)
<body>
<img class='img-item' src=''
data-original="https://ts1.cn.mm.bing.net/th/id/R-C.57384e4c2dd256a755578f00845e60af?rik=uy9%2bvT4%2b7Rur%2fA&riu=http%3a%2f%2fimg06file.tooopen.com%2fimages%2f20171224%2ftooopen_sy_231021357463.jpg&ehk=whpCWn%2byPBvtGi1%2boY1sEBq%2frEUaP6w2N5bnBQsLWdo%3d&risl=&pid=ImgRaw&r=0">
<img class='img-item' src=''
data-original="https://img.zcool.cn/community/01a3a2599c3deba80121ad7b597cab.jpg@1280w_1l_2o_100sh.jpg" alt="">
<img class='img-item' src=''
data-original="https://ts1.cn.mm.bing.net/th/id/R-C.66d7b796377883a92aad65b283ef1f84?rik=sQ%2fKoYAcr%2bOwsw&riu=http%3a%2f%2fwww.quazero.com%2fuploads%2fallimg%2f140305%2f1-140305131415.jpg&ehk=Hxl%2fQ9pbEiuuybrGWTEPJOhvrFK9C3vyCcWicooXfNE%3d&risl=&pid=ImgRaw&r=0"
alt="">
<img class='img-item' src=''
data-original="https://ts1.cn.mm.bing.net/th/id/R-C.ed4afdb04224a233524d8129044dd7fd?rik=P232HAfi6vm6Gg&riu=http%3a%2f%2fimg.sj33.cn%2fuploads%2fallimg%2f201505%2fReflecting-11.jpg&ehk=w21%2bLkUTsMeQ3tF17%2fbWyCIhEJeid3kmTGIhqrDo9YQ%3d&risl=&pid=ImgRaw&r=0"
alt="">
<img class='img-item' src=''
data-original="https://img.zcool.cn/community/01df7b56de44db6ac72531cb2906b9.JPG@1280w_1l_2o_100sh.jpg" alt="">
<img class='img-item' src='' data-original="https://b.zol-img.com.cn/sjbizhi/images/11/1080x1920/1592967802496.jpg"
alt="">
<img class='img-item' src='' data-original="https://b.zol-img.com.cn/sjbizhi/images/11/1080x1920/1592967802496.jpg"
alt="">
<img class='img-item' src=''
data-original="https://tse1-mm.cn.bing.net/th/id/OIP-C.6szqS1IlGtWsaiHQUtUOVwHaQC?rs=1&pid=ImgDetMain" alt="">
<img class='img-item' src='' data-original="https://sc.68design.net/photofiles/202102/2chxa2V1pR.jpg" alt="">
</body>
既然提到了src会阻塞页面加载,那我就不给值了,你怎么阻塞?但是没有src地址,该从哪里取图片呢?很简单,我把地址存在别的地方不就行了?标签支持我们添加任何属性,不管你叫铁柱还是狗蛋,都能给标签加上去,那我就语义化一点,给每个img标签添加一个data-original属性并用这个属性来存储图片地址。如此一来,地址就有了。
接下来要干的事情就是获取到用户所浏览的位置了。换句话说就是要知道获取这张照片是否能被用户看到从而控制图片是否加载。这并不难,原生JS足以实现。
首先需要拿到每个图片的dom元素。需要知道的是querySelectorAll
获取到的是所有img标签组成的数组,接下来对imgs数组进行遍历,我们可以通过getBoundingClientRect
方法获取dom的几何元素,其中有一个属性top,指的是当前这个dom元素的顶部距离屏幕顶部的距离,那么,也就意味着当top的值大于屏幕高度的时候,这张照片就还没进入用户的视野,而小于屏幕高度的时候就说明用户已经能看到这张照片了,就需要开始加载了。
function lazyLoad() {
let imgs = document.querySelectorAll('img[data-original]')
//只拿具有data-original属性的img
imgs.forEach((el) => {
let rect = el.getBoundingClientRect()//获取容器的几何信息
if (rect.top < viewHight) {
el.src = el.dataset.original
//这里的dataset.就是html中的data-,是一种特有的写法
el.removeAttribute('data-original')
//修改成功后移除data-original属性,否则后续可能再次被拿到并修改,造成资源浪费
}
})
}
lazyLoad()
document.addEventListener('scroll', lazyLoad)
//监听页面的滚动行为,每次滚动都执行一遍lazyLoad
这样虽然可以实现懒加载的效果,但还不够优雅,原因就在于当src被重新赋值的时候依旧会卡住。我们需要让src的地址完全加载完再显示。以下是优化后的代码
<script>
// 判断图片是否进入可视区域,有的话就将data-original赋给src
let viewHight = window.innerHeight//只有window有这个属性
function lazyLoad() {
let imgs = document.querySelectorAll('img[data-original]')//只拿具有data-original属性的img
imgs.forEach((el) => {
let rect = el.getBoundingClientRect()//获取容器的几何信息
if (rect.top < viewHight) {
let image = new Image()
image.src = el.dataset.original//这里的dataset.就是html中的data-,是一种特有的写法
image.onload = function () {
//当图片加载完成之后,再将地址赋值给src
el.src = image.src
}
el.removeAttribute('data-original')
}
}
}
lazyLoad()
document.addEventListener('scroll', lazyLoad)
</script>
需要知道的是img这个标签比较独特,有自己的构造函数,而不需要用document.createElement去创建元素,如此一来用户看到的就只有空白或者完整的图片,而不会看到半张图片。
总结
图片懒加载并不是一项很复杂的技术,但却可以很大程度上优化用户在浏览页面时的体验。避免了在糟糕的网络条件下的页面图片加载卡顿。正所谓十行代码实现功能,百行代码防止小人。希望这篇文章能够帮助到大家,祝大家都0error(s),0warning(s)
转载自:https://juejin.cn/post/7362722064069738533