当前位置: 首页 > Web前端 > HTML5

前端性能优化之图片延迟加载

时间:2023-04-05 22:18:07 HTML5

本文收录于githubgithub.com/Michael-lzg...demo源码地址github.com/Michael-lzg...在电商项目中,有经常会出现很多图片,比如banner广告图片、菜单导航图片、美团等商家listing页眉图片等,图片过多、图片过大往往会影响页面的加载速度,导致用户体验不好,所以优化图片的延迟加载势在必行。为什么我们需要延迟加载图像?我们先看一下页面启动时加载的图片信息。如图所示,页面启动时加载了几十张(甚至更多)图片,而且这些图片请求几乎是并发的。在Chrome浏览器中,支持的最大并发请求数是有限制的,其他请求会在push队列中等待或者停滞,直到上一轮请求完成后才会发出新的请求。因此,相当一部分图片资源请求需要排队等候。从上面可以看出,有的图片达到几百kB,设置为2M(这锅一定要操作,还要上传高清大图?),直接导致加载时间过长。针对以上情况,图片延迟加载有以下优点:减少资源的加载,页面启动时只加载首屏图片,可以显着降低服务器的压力和流量,同时也减少浏览器的负担。防止并发加载的资源过多阻塞js的加载,影响整个网站的启动。它可以改善用户体验。想象一下,当用户打开页面时,如果需要加载页面中所有的图片,由于图片数量多,等待时间非常长,严重影响用户体验。图片懒加载原理图片懒加载原理主要是判断当前图片是否已经到达可见区域,这是获取所有图片圆顶的核心逻辑。遍历每张图片,判断当前图片是否在可见区域内。如果是这样,请设置图像的src属性。绑定window的滚动事件并监听。我们先来看下页面结构Lazyload</body>首先获取所有图片的dom,通过document.body.clientHeight获取可见区域的高度,然后使用element.getBoundingClientRect()接口直接获取top值元素相对于浏览器,遍历每张图片,判断当前图片是否在可视区域内代码如下:functionlazyload(){letviewHeight=document.body.clientHeight//获取可视区域的高度letimgs=document.querySelectorAll('img[data-src]')imgs.forEach((item,index)=>{if(item.dataset.src==='')return//用于获取页面中某个元素相对于浏览器窗口的左上右下位置letrect=item.getBoundingClientRect()if(rect.bottom>=0&&rect.topdelay){prevTime=currTimefn.apply(context,args)clearTimeout(timer)return}timer=setTimeout(function(){prevTime=Date.now()timer=nullfn.apply(context,args)},delay)}}然后修改srcoll事件window.addEventListener('scroll',throttle(lazyload,200))IntersectionObserver通过上面例子的实现,我们需要监听scroll事件来实现懒加载,虽然我们可以通过Functionthrottling来防止函数的高频执行,但是我们仍然需要计算scrollTop,offsetHeight等属性。有没有一种不需要计算这些属性的简单方法?答案是IntersectionObserver。IntersectionObserver是一个新的API,可以自动“观察”一个元素是否可见,Chrome51+已经支持了。由于可见(visible)的本质是目标元素和视口产生一个相交区域,所以这个API被称为“相交观察者”。我们来看看它的用法:vario=newIntersectionObserver(callback,option)//开始观察io.observe(document.getElementById('example'))//停止观察io.unobserve(element)//关闭观察者io.disconnect()IntersectionObserver是浏览器原生提供的构造函数,它接受两个参数:callback为可见性变化时的回调函数,option为配置对象(该参数可选)。观察者的回调函数callback在目标元素的可见性发生变化时被调用。回调通常会触发两次。一次是目标元素刚进入视口(开始可见),一次是完全离开视口(开始不可见)。vario=newIntersectionObserver((entries)=>{console.log(entries)})回调函数的参数(entries)是一个数组,每个成员是一个IntersectionObserverEntry对象。例如,如果两个观察对象的可见性同时发生变化,则条目数组将有两个成员。time:可见性发生变化的时间,是毫秒级的高精度时间戳target:观察到的目标元素,是DOM节点对象isIntersecting:目标是否可见rootBounds:根的矩形区域信息element,getBoundingClientRect()方法的返回值,如果没有根元素(即相对于视口直接滚动),则返回nullboundingClientRect:目标元素的矩形区域信息intersectionRect:信息目标元素与视口(或根元素)相交区域的比例intersectionRatio:target元素可见比例,即intersectionRect与boundingClientRect之比,完全可见时为1,完全可见时小于等于0无形的。接下来我们使用IntersectionObserver实现图片的懒加载。constimgs=document.querySelectorAll('img[data-src]')constconfig={rootMargin:'0px',threshold:0,}letobserver=newIntersectionObserver((entries,self)=>{entries.forEach((entry)=>{if(entry.isIntersecting){letimg=entry.targetletsrc=img.dataset.srcif(src){img.src=srcimg.removeAttribute('data-src')}//取消观察self.unobserve(entry.target)}})},config)imgs.forEach((image)=>{observer.observe(image)})懒加载说明除了常用的v-show,v-bind,v-for等Vue中的指令,也可以自定义指令。Vue指令定义函数提供了几个钩子函数(可选):bind:只调用一次,当指令第一次绑定到元素时,可以定义一个初始化动作,绑定时执行一次。inserted:绑定元素插入到父节点时调用(如果父节点存在,则可以调用,不一定存在于文档中)。update:绑定元素所在模板更新时调用,不管绑定值是否变化。通过比较更新前后的绑定值。componentUpdated:当绑定元素所在的模板完成一个更新周期时调用。unbind:仅在指令与元素解除绑定时调用一次。实现懒加载指令的思路是判断浏览器是否支持IntersectionObserverAPI。如果支持,使用IntersectionObserver实现延迟加载。否则,使用srcoll事件监听+节流的方式。通过Vue.directive注册一个v-lazy指令,并暴露一个install()函数供Vue调用。在main.js中,可以调用use(directive)。将组件中标签的src替换为v-lazy,实现图片的懒加载。代码如下新建LazyLoad.js文件constLazyLoad={//安装方法install(Vue,options){constdefaultSrc=options.defaultVue.directive('lazy',{bind(el,binding){LazyLoad.init(el,binding.value,defaultSrc)},inserted(el){if(IntersectionObserver){LazyLoad.observe(el)}else{LazyLoad.listenerScroll(el)}},})},//初始化init(el,val,def){el.setAttribute('data-src',val)el.setAttribute('src',def)},//利用IntersectionObserver监听elobserve(el){vario=newIntersectionObserver((entries)=>{constrealSrc=el.dataset.srcif(entries[0].isIntersecting){if(realSrc){el.src=realSrcel.removeAttribute('data-src')}}})io.observe(el)},//监听滚动事件listenerScroll(el){consthandler=LazyLoad.throttle(LazyLoad.load,300)LazyLoad.load(el)window.addEventListener('scroll',()=&g吨;{handler(el)})},//加载真实图片load(el){constwindowHeight=document.documentElement.clientHeightconstelTop=el.getBoundingClientRect().topconstelBtm=el.getBoundingClientRect().bottomconstrealSrc=el.dataset.srcif(elTop-windowHeight<0&&elBtm>0){if(realSrc){el.src=realSrcel.removeAttribute('data-src')}}},//节流节流阀(fn,delay){让定时器让prevTime返回函数(...args){constcurrTime=Date.now()constcontext=thisif(!prevTime)prevTime=currTimeclearTimeout(timer)if(currTime-prevTime>delay){prevTime=currTimefn.apply(context,args)clearTimeout(timer)返回}timer=setTimeout(function(){prevTime=Date.now()timer=nullfn.apply(context,args)},delay)}},}exportdefaultLazyLoad在main.js里使用命令importLazyLoadfrom'./LazyLoad.js'Vue.use(LazyLoad,{default:'xxx.png',})将组件中标签的src替换为v-lazy这样就可以完成一个Vue懒加载命令了。总结要提高网站的加载性能,图片的懒加载是很有必要的。图片懒加载的原理是判断当前图片是否到达可见区域进行加载,通过监听scroll事件和IntersectionObserver实现相应的功能。图片的懒加载指令可以通过Vue.directive编写。推荐文章w你必须知道的webpack插件原理webpack异步加载原理及分包策略解析18款webpack插件汇总,总有你想要的!搭建一个vue-cli4+webpack移动端框架(开箱即用)从零搭建到优化一个类似vue-cli的脚手架封装一个toast和dialog组件发布到npm从头搭建一个webpack项目总结几个webpack包优化方法vue知识体系进阶应用总结vue知识体系实用技巧总结vue知识体系基础入门总结移动端H5开发常用技巧总结(干货满满!)