当前位置: 首页 > news >正文

前端性能优化之图像优化

图像优化问题主要可以分为两方面:图像的选取和使用,图像的加载和显示。

图像基础

HTTP Archive上的数据显示,网站传输的数据中,60%的资源都是由各种图像文件组成的,当然这些是将各类型网站平均的结果,单独只看电商类网站,这个比例可能会更大,如此之大的资源占比,同样意味着有很大的优化空间。

图像是否必需

图像资源优化的根本思想:压缩。无论是选取何种图像的文件格式,还是针对于同一种格式压缩至更小的尺寸,其本质都是用更小的资源开销来完成图像的传输和展示。
我们首先需要思考下要达到期望的信息传递效果,是否需要图像?这不仅是因为图像资源与网页上的其他资源(html/css/js等)相比有更大的字节开销,出于对节省资源的考虑,对用户注意力的珍惜也很重要,如果一个页面打开之后有很多图像,那么用户其实很难快速梳理出有效的信息,即便获取到了也会让用户觉得很累。一个低感官体验的网站,它的价值转化率不会很高。
当确定了图像的展示效果必须存在时,在前端实现上也并非一定就要用图像文件,还存在一些场景可以使用更高效的方式来实现所需的效果。

  • 网站中一个图像在不同的页面或不同的交互状态下,需要呈现不同的效果(边角的裁切、阴影或渐变),其实没有必要准备不同效果的图像,使用css即可,相对于一张图像文件的大小来讲,修改其所增加的css代码量忽略不计。
  • 如果一个图像上面需要显示文字,建议通过网页字体的形式通过前端代码进行添加,而不是使用带文字的图像,其原因一方面是包含了更多信息的图像文件一般会更大,另一方面图像中的文本信息代码的用户体验一般较差(不可选择、搜索以及缩放),并且在高分辨率设备上的显示效果也会大打折扣。

因此,我们在选择使用某种资源之前,如果期望达到更优的性能效果,则需要先思考这种选择是否必须。

矢量图和位图

当确定了图像是实现展示效果的最佳方式时,接下来就是选择合适的图像格式。图像文件可以分为两类:矢量图和位图。

  • 矢量图
    矢量图的优点时能够在任何缩放比例下呈现出细节同样清晰的展示效果。其缺点是对细节的展示效果不够丰富,对足够复杂的图像来说,比如要达到照片的效果,若通过svg进行矢量图绘制,则所得文件会大的离谱,但即便如此也很难达到照片的真实效果。目前几乎所有的浏览器都支持svg。
    svg标签所包括的部分就是该矢量图的全部内容,除了必要的绘制信息,可能还包括一些元数据,比如xml命名空间、图层以及注释信息。但这些信息对浏览器绘制一个svg来说不是必要的,所以在使用之前可通过工具去除这些元数据来达到压缩的目的。
  • 位图
    位图是通过对一个矩阵中的栅格进行编码来表示图像的,每个栅格只能编码表示一个特定的颜色,如果组成图像的栅格像素点越多且每个像素点所能表示的颜色范围越广,则位图图像的显示效果就会越逼真,当然图像文件也就越大。虽然位图没有像矢量图那种不受分辨率影响的优秀特性,但对于复杂的照片却能提供较为真实的细节体验。

分辨率

在前端开发过程中书写css时,经常会为图像设置显示所需的长宽像素值,但在不同的设备屏幕上,有时候相同的图像以及相同的设置,其渲染出来的图像会让人明显感觉出清晰度有差别。产生这个现象的原因涉及两种不同的分辨率:屏幕分辨率和图像分辨率。
图像分辨率展示的就是该图像文件所包含的真实像素值,比如一个200px200px的分辨率的图像文件,它就定义了长宽各200各像素点的信息。设备分辨率则是显示器屏幕所能显示的最大像素值,比如一台电脑的显示器分辨率为2560px1600px。更高的设备分辨率有助于显示更绚丽多彩的图像,这其实很适合矢量图的发挥,因为其不会失真。而对于位图来说,只有图像文件包含更多的像素信息时,才能更充分地利用屏幕分辨率。为了能在不同的分辨率下使项目中所包含的图像都能得到恰当的展示效果。可以利用picture标签和srcset属性提供图像的多个变体。

<img src='photo.jpg' srcset='photo@2x.jpg,photo@3x.jpg' alt='photo'/>

除了ie和其他较低版本的浏览器不支持,目前主流的大部分浏览器都已支持img标签的srcset属性。在srcset属性中设置多种分辨率的图像文件以及使用条件,浏览器在请求之前便会先对此进行解析,只选择最合适的图像文件进行下载,如果浏览器不支持,务必在src属性中包含必要的默认图片。
使用picture标签则会在多图像文件选择时,获得更多的控制维度,比如屏幕方向、设备大小、屏幕分辨率等。

<picture>
<source media="(min-width:800px)" srcset="photo.jpg,photo@2x.jpg"/>
<source media="(min-width:450px)" srcset="photo.jpg,photo@2x.jpg"/>
<img src='photo.jpg'/>
</picture>

由于picture标签也是加入标准不久的元素标签,所以在使用过程中,同样应考虑兼容问题。

无损和有损压缩

压缩时降低源文件大小的有效方式,对js代码或网页的一些脚本文件而言,压缩掉的内容是一些多余的空格以及不影响执行的注释,其目的是在不损坏正常执行的情况下,尽量缩小源文件的大小。对图像文件而言,由于人眼对不同颜色的敏感度存在差异,所以便可通过减少对某种颜色的编码位数来减少文件大小,甚至还可以损失部分源文件信息,以达到近似效果,使得压缩后的文件尺寸更小。

对于图像压缩,应该是使用有损压缩还是无损压缩,可以简单分为两步进行。

  1. 确定业务所需要展示图像的颜色阶数、图像显示的分辨率以及清晰程度,当锚定了这几个参数的基准后,如果获取的图像源文件的相应参数指标过高,便可适当进行有损压缩,通过降低源文件图像质量的方法来降低图像文件大小。
    如果业务所要求的图像质量较高,便可跳过有损压缩,直接进入第二步无损压缩。所以是否要进行有损压缩,其实是在理解了业务需求后的一个可选选项,而非必要的。
  2. 当确定了展示图像的质量后,便可利用无损压缩技术尽可能降低图像大小。无损压缩是应当完成的工作环节。因此最好能通过一套完整的工程方案,自动化执行来避免繁琐的人工重复工作。

CSS Sprite

雪碧图,通过将多张小图标拼接成一张大图,有效地减少http请求数量以达到加速显示内容的技术。

通常对于雪碧图的使用场景应当满足以下条件:首先这些图标不会随用户信息的变化而变化,它们属于网站通用的静态图标;同时单张图标体积尽量小,这样经过拼接后其性能的提升才会比较乐观;若加载量比较大则效果会更好。但是不建议将较大的图片拼接成雪碧图,因为大图拼接后的单个文件体积会非常大,这样占用网络带宽的增加与请求完成所耗费时间的延长,会完全淹没通过减少http请求次数所带来的性能提升。

雪碧图的使用方式十分简单:通过css的background-image属性引入雪碧图的url后,再使用background-position定位所需要的单个图标再雪碧图上的起始位置,配合width和height属性来锁定具体图标的尺寸。

.sprite-sheet{background-image:url(https://xxxxx);background-size: 24px 600px
}.icon-1 .sprite-sheet{background-position: 0 0;height:24px;width:24px;
}.icon-2 .sprite-sheet{background-position: 0 -24px;height:24px;width:24px;
}

使用雪碧图来提升小图标加载性能的历史由来已久。在http1.x环境下,它确实能够减少相应的http请求,但需要注意当部分图标变更时,会导致已经加载的雪碧图缓存失效。同时在http2中,最好的方式应该是加载单张图像文件,因为可以在一个http连接上发起多次请求,所以对于是否使用此方法,需要考虑具体的使用环境和网络设置。

图像格式选择建议

首先明确告诉读者:不存在适用于任何场景且性能最优的图像使用方式。所以作为开发者,想要网站性能在图像方面达到最优,如何根据业务场景选择合适的文件格式尤其重要,图像文件的使用策略如下图所示:
在这里插入图片描述
注意:使用webp格式的图像需要考虑到浏览器的兼容性。通常的处理思路分为两种:一种是在前端处理浏览器兼容性的判断,可以通过浏览器的全局属性window.navigator.userAgent获取版本信息,再根据兼容支持情况,选择是否请求webp图像格式的资源;也可以使用<picture/>标签来选择显示的图像格式。

<picture><source srcset="photo.webp" type="image/webp"/><img src='photo.jpg'/>
</picture>

除此之外,位图对于不同缩放比的响应式场景,建议提供多张不同尺寸的图像,让浏览器根据具体场景进行请求调用。

参考文章:https://blog.csdn.net/qq_42691298/article/details/128485051

Web字体

使用web字体有多种优点:增强网站的设计感、可读性,同时还能搜索和选取所表示的文本内容,且不受屏幕尺寸与分辨率的影响,能提供一致的视觉体验。除此之外,由于每个字型都是特定的矢量图标,所以可以将项目中用到的矢量图打包到一个web字体文件中使用,以节省对图标资源的http请求次数,这样做类似雪碧图优化目的。

  1. 字体的使用
    目前网络上常用的字体格式有:EOT、TTF、WOFF与WOFF2,由于存在兼容问题,并没有哪一种字体能够适用所有浏览器,所以在实际使用中,网站开发者会声明提供字体的多种文件格式,来达到一致性的体验效果。
    在web项目中,一般会先通过@font-face声明使用的字体系列:

    @font-face {font-family: 'myFont';src: url('./my-font.ttf') format('truetype');src: url('./my-font.woff') format('woff'),url('./my-font.woff2') format('woff2');font-weight: 600;font-style: normal;
    }.my-font {font-family: 'myFont';color: red;
    }

    在上述代码中通过src字段的属性值,可以指定字体资源的位置,并且该属性值还可以提供一个用逗号分隔的列表,列表中不同字体文件格式的资源顺序同样重要,浏览器将选取其所支持的第一个格式资源。如果希望较新的woff2格式被使用,则应当将woff2声明在woff之上。

  2. 子集内嵌
    如果将所有字型都打包成一个文件来请求使用,不免就会存在许多根本用不到的字型信息浪费带宽。相较于拉丁文字体而言,包含中文字符的字体文件的大小会格外突出。可以使用unicode-range属性定义所使用的字体子集。它支持三种形式:单一取值(如U+233)、范围取值(如U+233-2ff)、通配符范围(如U+2??),取值的含义是字体集文件中的代码索引点:

    @font-face {font-family: 'myFont';src: url('./my-font.ttf') format('truetype');src: url('./my-font.woff') format('woff'),url('./my-font.woff2') format('woff2');unicode-range:U+100-3ff,U+f??;font-weight: 600;font-style: normal;
    }
    

    通过使用子集内嵌,以及为字体的不同样式变体采用单独的文件,用户可以仅根据需要下载字体的子集,而不必强制他们下载可能永远都不会用到的字体子集。不过属性unicode-range也存在兼容问题,对于不支持的浏览器,可能需要手动处理字体文件。

  3. 字体文件预加载
    在默认情况下,构建渲染树之前会阻塞字体文件的请求,这将可能导致部分文本渲染延迟,对此可使用<link rel=“preload”>对字体资源进行加载。

<head>
<link rel="preload" href="xxxx" as ="font"/>
</head>

link需要和@font-face对字体的定义一同使用,它只负责提示浏览器需要预加载给定的资源,而不指明如何使用。需要注意的是,这样做将会无条件向网络发出字体请求如果项目迭代将原本使用的字体文件修改或删除,也需要同步删除对字体预加载的设置。

参考文章:https://blog.csdn.net/weixin_46820017/article/details/116666903

注意display:none的使用

在使用位图时,经常会根据屏幕尺寸,权限控制等不同条件,响应式地处理资源的展示与隐藏。出于对性能的考虑,希望对于不展示的图像,尽量避免在首屏时进行资源请求加载。

<div style="display:none"><img src="img.png"/>
</div>

根据html的解析顺序,img.png的图像文件会被请求。

<div style="display:none"><div style="background:url(img.png)"/>
</div>

css解析后发现父级使用了none,再去计算子级的样式就没有多大意义了,所以就不会去下载子级div的背景图片。

如果不清楚不同浏览器对display:none关于图像加载的控制,则可以通过开发者工具进行验证。
笔者这里推荐的做法是使用<picture/>或<img srcset>的方式进行响应式显示。

图像延迟加载

在首次打开网站时,应尽量只加载首屏内容所包含的资源,而首屏之外涉及的图片或视频,可以等到用户滚动视窗浏览时再去加载。

实现图片的延迟加载:传统方式

通过监听的方式,通过监听scroll事件与resize事件,并在事件的回调函数中去判断,需要进行延迟加载的图片是否进入视窗区域。

首先定义出将要实现延迟加载的<img>标签结构:

<img class="lazy" alt="" src="xxxx" data-src="xxxx"/>
  • src属性,加载前的占位符图片,可用base64图片或低分辨率的图片。
  • data-src属性,通过该自定义属性保存图片真实的url外链。

对于只可上下滚动的页面,判断一个图片元素是否出现在屏幕视窗中的方法其实显而易见,即当元素上边缘距屏幕视窗顶部的top值小于整个视窗的高度window.innerHeight时,预加载的事件处理代码如下:

//在dom内容加载完毕后,执行延迟加载处理逻辑
document.addEventListener("DOMContentLoaded",function(){//获取所有需要延迟加载的图片let lazyImages = [].slice.call(document.querySelectorAll("img.lazy"));//限制函数频繁被调用let active= false;const lazyLoad = function(){if(active==false){active = true;setTimeout(function(){lazyImages.forEach(function(lazyImage){//判断图片是否出现在视窗中if((lazyImage.getBoundingClientRect().top<=window.innerHeight&&lazyImage.getBoundingClientRect().bottom>=0)&&getComputedStyle(lazyImage).display!=='none'){//将真实的图片url赋值给src属性,发起请求加载资源lazyImage.src = lazyImage.dataset.src;lazyImage.classList.remove("lazy");lazyImages = lazyImages.filter(function(image){return image !== lazyImage;})//所有延迟加载图片加载完成后,移除事件触发处理函数if(lazyImages.length===0){document.removeEventListener("scroll",lazyLoad);document.removeEventListener("resize",lazyLoad);document.removeEventListener("orientationchange",lazyLoad);}}});active=false;},200)}};document.addEventListener("scroll",lazyLoad);document.addEventListener("resize",lazyLoad);document.addEventListener("orientationchange",lazyLoad);
})

由于无法控制用户随心所欲地滑动鼠标滚轮,从而造成scroll事件被触发地过于频繁,导致过多的冗余计算影响性能。所以通过active标志位的方式进行限流。
即便如此也有潜在的性能问题,因为重复的setTimeout调用时浪费的,虽然进行了触发限制,但当文档滚动或窗口大小调整时,不论图片是否出现在视窗中,每200ms都会执行一次检查,并且跟踪尚未加载的图片数量,以及完全加载完后,取消绑定滚动事件的处理函数等操作都需要开发者来考虑。
如此看来,虽然传统的延迟加载方式具有良好的浏览器兼容性,但是实现起来比较琐碎。

实现图片的延迟加载:IntersectionObserver方式

现代浏览器大多支持了IntersectionObserver API,可以通过它来检查目标元素的可见性,这种方式的性能和效率都比较好。
IntersectionObserver:每当因页面滚动或窗口尺寸发生变化,使得目标元素与设备视窗或其他指定元素产生交集时,便会触发通过IntersectionObserver API配置的回调函数,在该函数中进行延迟加载的逻辑处理,会比传统方式显得更加简洁而高效。

//在dom内容加载完毕后,执行延迟加载处理逻辑
document.addEventListener("DOMContentLoaded",function(){//获取所有需要延迟加载的图片let lazyImages = [].slice.call(document.querySelectorAll("img.lazy"));//判断浏览器兼容性if("IntersectionObserver" in window && "IntersectionObserverEntry" in window && "intersectionRatio" in window.IntersectionObserverEntry.prototype){let lazyImageObserver = new IntersectionObserver(function (entries,observer){entries.forEach(function(entry){//判断图片是否出现在视窗中if(entry.isIntersecting){let lazyImage = entry.target;lazyImage.src = lazyImage.dataset.src;lazyImage.classList.remove("lazy");lazyImageObserver.unobserver(lazyImage);}})})lazyImages.forEach(function(lazyImage){lazyImageObserver.observer(lazyImage)})}
})

这种方式判断元素是否出现在视窗中更为简单直观,应在实际开发中尽量使用。但其问题是并非所有浏览器都能兼容。在将这种方式引入项目之前,应当确保已做到以下两点:

  1. 做好尽量完备浏览器兼容性检查,对于兼容IntersectionObserver API的浏览器,采取这种方式处理,而对于不兼容的浏览器,则使用传统的实现方式。
  2. 使用相应兼容的polyfill插件。

实现图片的延迟加载:CSS类名方式

这种方式通过css的background-image属性来加载图片,与判断<img>标签的src属性是否有要请求图片的url不同,css中图片加载的行为建立在浏览器对文档分析基础之上。
具体来说,当dom树,cssom树以及渲染树生成后,浏览器会去检查css以何种方式应用于文档,再决定是否请求外部资源。如果浏览器确定涉及外部资源请求的css规则再当前文档中不存在,便不会去请求该资源。

<div class="wrap"><div class="lazy-background one"/><div class="lazy-background two"/><div class="lazy-background three"/>
</div>

具体的实现方式是通过js来判断元素是否出现在视窗中,当在视窗中时,为其元素的class属性添加visible类名。而在css文件中,为同一类名元素定义出带.visible和不带.visible的两种包含background-image规则。
不带.visible的图片规则中的background-image属性可以是低分辨率的图片或base64图片,而带.visible的图片规则中的background-image属性为希望展示的真实图片的url。

//在dom内容加载完毕后,执行延迟加载处理逻辑
document.addEventListener("DOMContentLoaded",function(){//获取所有需要延迟加载的图片let lazyImages = [].slice.call(document.querySelectorAll("img.lazy"));//判断浏览器兼容性if("IntersectionObserver" in window && "IntersectionObserverEntry" in window && "intersectionRatio" in window.IntersectionObserverEntry.prototype){let lazyImageObserver = new IntersectionObserver(function (entries,observer){entries.forEach(function(entry){//判断图片是否出现在视窗中if(entry.isIntersecting){entry.target.classList.add("visible")lazyImageObserver.unobserver(entry.target);}})})lazyImages.forEach(function(lazyImage){lazyImageObserver.observer(lazyImage)})}
})

原生的延迟加载支持

除了上述通过开发者手动实现延迟加载逻辑的方式,从chrome75版本开始,已经可以通过<img>和<iframe>标签的loading属性原生支持延迟加载了,loading属性包含了以下三种取值。

  • lazy:进行延迟加载
  • eager:立即加载
  • auto:浏览器自行决定是否进行延迟加载(默认值)

兼容性处理:通过使用新技术优化了延迟加载的实现方式,同时也应该注意新技术在不同浏览器之间的兼容性,在使用前需要对浏览器特性进行检查。

<script>
if("loading" in HTMLImageElement.prototype){//浏览器支持loading的延迟加载方式
}else{//获取其他js库来实现延迟加载
}
</script>

视频加载

不需要自动播放

由于chrome等一些浏览器会对视频资源进行预加载,即在html完成加载和解析时触发DOMContentLoaded事件开始请求视频资源,当请求完成后触发window.onload事件开始页面渲染。

为了使页面更快地加载并渲染出来,可以阻止不需要自动播放的视频的预加载。其方法是通过视频标签的preload进行控制:

<video preload="none" poster="default.png"><source src="simply.webm" type="video/webm" /><source src="simply.mp4" type="video/mp4" />
</video>

preload属性通常的默认值是auto,表示无论用户是否希望,所有视频文件都会被自动下载,这里将其设置为none,来阻止视频的自动预加载。同时还通过poster属性为视频提供占位符图片。

  • chrome之前的版本中,preload的默认值是auto,从64版本以后其默认值改为了metadata,表示仅加载视频的元数据,火狐、ie11和edge等浏览器的行为类似。
  • safari11.0的mac版本会默认进行部分视频资源预加载,11.2的mac版后仅可预加载元数据,但ios的safari不会对视频预加载。
  • 若浏览器开启了省流量模式,preload将默认值设置为none。

当浏览器支持preload的metadata属性值后,这将会是一种兼顾了性能和体验后更优的方式,因为从体验上说,对于不自动播放的视频场景,在单击播放前,若能提前告知视频的播放时长、播放列表等元数据,便能带来更好的用户体验。
另外,如果站点中包含了同一域名下的多个视频资源,那么最好将preload设置为metadata,或者定义poster属性值时将preload设置为none,这样可以很好地避免http地最大连接数,因为通常http1.1协议规定同一域名下地最大连接数为6,如果同时有超过此数量地资源请求连接,那么多余地连接便会被挂起,这无疑会对性能造成负面影响。

视频代替GIF动画

应当尽量用视频代替尺寸过大地gif动画。虽然gif动画的应用历史和范围都很广泛,但其在输出文件大小、图像色彩质量等许多方面的表现都不如视频。gif动画相对于视频具有三个附加的特性:没有音轨、连续循环播放、加载完自动播放,替换成视频后类似于:

<video autoplay muted loop playsinline width="610" height="254" poster="default.png"><source src="simply.webm" type="video/webm" /><source src="simply.mp4" type="video/mp4" />
</video>

autoplay自动播放、muted静音播放以及loop循环播放,而playsinline属性则用于在ios中指定自动播放的。注意并非所有浏览器都像chrome一样,能够自动进行延迟加载,因此我们可以根据上面的提及到的IntersectionObserver的方式实现延迟加载的控制。

//在dom内容加载完毕后,执行延迟加载处理逻辑
document.addEventListener("DOMContentLoaded",function(){//获取所有需要延迟加载的图片let lazyImages = [].slice.call(document.querySelectorAll("video.lazy"));//判断浏览器兼容性if("IntersectionObserver" in window && "IntersectionObserverEntry" in window && "intersectionRatio" in window.IntersectionObserverEntry.prototype){let lazyImageObserver = new IntersectionObserver(function (entries,observer){entries.forEach(function(entry){//判断图片是否出现在视窗中if(entry.isIntersecting){for(const source in entry.target,children){const videoSrc = entry.target.children[source];if(typeof videoSrc.tagName==='string' && videoSrc.tagName==='source'){videoSrc.src = videoSrc.dataset.src;}}entry.target.load();//需要手动调用load方法entry.target.classList.remove("lazy")lazyImageObserver.unobserver(entry.target);}})})lazyImages.forEach(function(lazyImage){lazyImageObserver.observer(lazyImage)})}
})

加载注意事项

首屏加载

对于首屏上的内容就不应当进行延迟加载,而应该正常加载。这样处理的原因是:延迟加载会将图像或视频等媒体资源延迟到dom可交互之后,即脚本完成加载并开始执行时才会进行。所以对首屏视窗之外的媒体资源采用延迟加载,而对首屏视窗内的媒体资源采用正常的方式加载,会带来更好的性能体验。
由于网站页面所呈现的设备屏幕尺寸多种多样,因此如何判断首屏视窗的边界就会因设备的不同而有所不同。目前也没有完全行之有效的方法来完美地处理每种设备的情况。
此外,若将首屏视窗边界线作为延迟加载触发的阈值,其实并非最佳的性能考虑。更为理想的做法是,在延迟加载的媒体资源到达首屏边界之前设置一个缓冲区,以便媒体资源在进入视窗之前就开始进行加载。

let lazyImageObserver = new IntersectionObserver(function (entries,observer){
},{rootMargin:"0 0 256px 0"})//当媒体元素距离视窗下边界小于256px时,回调函数会执行

内容加载失败

const newImage = new Image();
newImage.src="photo.jpg"
//当发生故障时的处理措施
newImage.onerror=(err)=>{
}
//图像加载后的回调
newImage.onload=()=>{
}

当图片资源未能按预期成功加载时,所采取的具体处理措施应当依据应用场景而定。比如,当请求的媒体资源无法加载时,可将使用的图像占位符替换成按钮,或者在占位符区域显示错误的提示信息。

图像解码延迟

图像从被浏览器请求获取,再到最终完整呈现在屏幕上,需要经历一个解码的过程,图像的尺寸越大,所需要的解码时间就越长。如果在js中请求加载较大的图像文件,并把它直接放入dom结构中后,那么将可能占用浏览器的主进程,进而导致解码期间用户界面出现短暂的无响应。

<button id="load-image">加载图像
</button>
<div id="image-container"/>

对于的js事件处理代码如下:

document.addEventListener("DOMCOntentLoaded",()=>{const loadButton = document.getElementById("load-image");const imageContainer = document.getElementById("image-container");const newImage = new Image();newImage.src = "https://sssss"loadButton.addEventListener("click",function(){if("decode" in newImage){//异步解码方式newImage.decode().then(()=>{imageContainer.appendChild(newImage);})}else{//正常图像加载方式imageContainer.appendChild(newImage);}},{once:true})
})

需要说明的是,如果网站所包含的大部分图像尺寸都很小,那么使用这种方式的帮助并不大,同时还会增加代码的复杂性,但可以肯定的是这么做会减少延迟加载大型图像文件所带来的卡顿。

js是否可用

在通常情况下,js都是始终可用的,但在一些异常不可用的情况下,开发者应当做好适配,不能始终在延迟加载的图像位置上展示占位符。可以考虑使用<noscript>标记,在js不可用时提供图像的真实展示:

<!-- 使用延迟加载的图像文件标签 -->
<img class = "lazy" src="aaa.png" data-src = "load.png" alt="xxx"/>
<!-- js不可用时,原生展示目标图像 -->
<noscript><img src="load.png" alt="xxxx"/>
</noscript>

如果上述代码同时存在,当js不可用时,页面中会同时展示图像占位符和<noscript>中包含的图像,为此可以给<html>标签添加一个no-js类:

<html class="no-js">

在由<link>标签请求css文件之前,在<head>标签结构中放置一段内联脚本,当js可用时,用于移除no-js类:

<script>document.documentElement.classList.remove("no-js")
</script>

以及添加必要的css样式,使得在js不可用时屏蔽包含.lazy类元素的显示:

.no-js .lazy{display:none;
}

资源优先级

浏览器向网络请求到所有数据,并非每个字节都具有相同的优先级或重要性。所以浏览器通常都会采取启发式算法,对所要加载的内容先进行推测,将相对重要的信息优先呈现给用户。比如浏览器一般会先加载css文件,然后再去加载js脚本和图像文件。
但即便如此,也无法保证启发式算法在任何情况下都是准确有效的。

预加载

使用<link rel=“preload”>标签告诉浏览器当前所指定的资源,应该拥有更高的优先级,例如:

 <link rel="preload" as="script" href="test.js"/><link rel="preload" as="style" href="test.css"/><!-- 通常字体文件都位于页面加载的若干css文件的末尾,当为了减少用户等待文本内容的时间,就必须要提取获取字体 --><link rel="preload" as="font" type="font/woff2" crossorigin="crossorigin" href="test.woff2"/>

这里通过as属性告诉浏览器所要加载的资源类型,该属性值所指定的资源类型应该与要加载的资源相匹配,否则浏览器是不会预加载该资源的。在这里需要注意的是,<link rel=“preload”>会强制浏览器进行预加载,它与其他对资源的提示不同,浏览器对此是必须执行的。因此,在使用时应该尽量仔细测试,以确保使用该指令时不会提取不需要的内容或重复的内容。
如果预加载指定的资源在3s内未被当前页面使用,则浏览器会在开发者工具的控制台中进行告警提示,该告警提示务必要处理。

预连接

通常在速度较慢的网络环境中建立连接会非常耗时,如果建立安全连接将更加耗时。其原因是整个过程会涉及到dns查询,重定向和与目标服务器之间建立连接的多次握手,所以若能提前完成上述这些功能,则会给用户带来更加流畅的浏览体验,同时由于建立连接的大部分时间消耗是等待而非交换数据,这样也能有效地优化带宽的使用情况。

 <link rel="preconnect" href="https://www.xxxx"/>

通过该指令,告诉浏览器当前页面将与站点建立连接,希望尽快启动该过程。虽然这么做的成本较低,但会消耗宝贵的cpu时间,特别是在建立https安全连接时。如果建立好连接后的10s内,未能及时使用连接,那么浏览器关闭该连接后,之前未建立连接所消耗的资源相当于完全浪费掉了。
另外,还有一种与预连接相关的类型<link rel=“dns-prefetch”>,也就是常说的dns预解析,它仅用来处理dns查询,但由于其受到浏览器的广泛支持,且缩短了dns的查询时间的效果显著,所以使用场景十分普遍。

预提取

前面介绍的预加载和预连接,都是试图使所需的关键资源或关键操作更快地获取或发生,这里介绍地预提取,则是利用机会让某些非关键操作能够更早发生。
这个过程的实现方式是根据用户已经发生的行为来判断其接下来的预期行为,告诉浏览器稍后可能需要的某些资源。也就是在当前页面加载完成后,且在宽带可用的情况下,这些资源将以Lowest的优先级进行提起。

显而易见,预提取最合适的场景是为用户下一步可能进行的操作做好必要的准备,如在电商平台的搜索框中查询商品,可预提取查询结果列表中的首个商品详情页;或者使用搜索查询时,预提取查询结果的分页内容的下一页:

<link rel="prefetch" href="page-2.html"/>

需要注意的是,预提取不能递归使用,比如在搜索查询的首页page-1.html时,可以预提取当前页面的下一页page-2.html的html内容,但对其中所包含的任何额外资源不会提前下载,除非有额外明确指定的预提取。
另外,预提取不会降低现有资源的优先级,比如在如下html中:

<html><head><link rel="prefetch" href="style.css"/><link rel="stylesheet" href="style.css"/>      </head>
</html>

可能读者会觉得对style.css的预提取声明,会降低接下来<link rel=“stylesheet”>的优先级,当其实的情况是,该文件会被提取两次,第二次可能会使用缓存。显然两次提取对用户体验来说时非常糟糕的,因为这样不但需要等待阻塞渲染的css,而且如果第二次提取没有命中缓存,必然会产生带宽的浪费,所以在使用时应充分考虑。

相关文章:

前端性能优化之图像优化

图像优化问题主要可以分为两方面&#xff1a;图像的选取和使用&#xff0c;图像的加载和显示。 图像基础 HTTP Archive上的数据显示&#xff0c;网站传输的数据中&#xff0c;60%的资源都是由各种图像文件组成的&#xff0c;当然这些是将各类型网站平均的结果&#xff0c;单独…...

微信小程序封装vant 下拉框select 单选组件

先上效果图&#xff1a; 主要是用vant 小程序组件封装的&#xff1a;vant 小程序ui网址&#xff1a;vant-weapp 主要代码如下: 先封装子组件&#xff1a; select-popup 放在 components 文件夹里面 select-popup.wxml: <!--pages/select-popup/select-popup.wxml--> &…...

c语言试卷

江西财经大学IT帮 2020&#xff0d;2021第一学期期末C语言模拟考试试卷 课程名称&#xff1a;C语言程序设计(软件)&#xff08;主干课程&#xff09; 适用对象&#xff1a;21级本科 试卷命题人 钟芳盛 游天悦 李俊贤 万军豪 张位 试卷审核人 钟芳盛 一、单项…...

文献阅读:Sparse Low-rank Adaptation of Pre-trained Language Models

文献阅读&#xff1a;Sparse Low-rank Adaptation of Pre-trained Language Models 1. 文章简介2. 具体方法介绍 1. SoRA具体结构2. 阈值选取考察 3. 实验 & 结论 1. 基础实验 1. 实验设置2. 结果分析 2. 细节讨论 1. 稀疏度分析2. rank分析3. 参数位置分析4. 效率考察 4.…...

NCC基础开发技能培训

YonBuilder for NCC 是一个带插件的eclipse工具&#xff0c;跟eclipse没什么区别 NC Cloud2021.11版本开发环境搭建改动 https://nccdev.yonyou.com/article/detail/495 不管是NC Cloud 新手还是老NC开发&#xff0c;在开发NC Cloud时开发环境搭建必看&#xff01;&#xff…...

Flink中的状态管理

一.Flink中的状态 1.1 概述 在Flink中&#xff0c;算子任务可以分为有状态和无状态两种状态。 无状态的算子任务只需要观察每个独立事件&#xff0c;根据当前输入的数据直接转换输出结果。例如Map、Filter、FlatMap都是属于无状态算子。 而有状态的算子任务&#xff0c;就…...

【linux】线程互斥

线程互斥 1.线程互斥2.可重入VS线程安全3.常见锁的概念 喜欢的点赞&#xff0c;收藏&#xff0c;关注一下把&#xff01; 1.线程互斥 到目前为止我们学了线程概念&#xff0c;线程控制接下来我们进行下一个话题&#xff0c;线程互斥。 有没有考虑过这样的一个问题&#xff0c…...

机器学习原理到Python代码实现之LinearRegression

Linear Regression 线性回归模型 该文章作为机器学习的第一篇文章&#xff0c;主要介绍线性回归模型的原理和实现方法。 更多相关工作请参考&#xff1a;Github 算法介绍 线性回归模型是一种常见的机器学习模型&#xff0c;用于预测一个连续的目标变量&#xff08;也称为响应变…...

Hive SQL / SQL

1. 建表 & 拉取表2. 插入数据 insert select3. 查询3.1 查询语句语法/顺序3.2 关系操作符3.3 聚合函数3.4 where3.5 分组聚合3.6 having 筛选分组后结果3.7 显式类型转换 & select产生指定值的列 4. join 横向拼接4.1 等值连接 & 不等值连接4.2 两表连接4.2.1 内连…...

程序媛的mac修炼手册--MacOS系统更新升级史

啊&#xff0c;我这个口罩三年从未感染过新冠的天选免疫王&#xff0c;却被支原体击倒&#x1f637;大意了&#xff0c;前几天去医院体检&#xff0c;刚检查完出医院就摘口罩了&#x1f926;大伙儿还是要注意戴口罩&#xff0c;保重身体啊&#xff01;身体欠恙&#xff0c;就闲…...

【数据库原理】(9)SQL简介

一.SQL 的发展历史 起源&#xff1a;SQL 起源于 1970 年代&#xff0c;由 IBM 的研究员 Edgar F. Codd 提出的关系模型概念演化而来。初期&#xff1a;Boyce 和 Chamberlin 在 IBM 开发了 SQUARE 语言的原型&#xff0c;后发展成为 SQL。这是为了更好地利用和管理关系数据库。…...

第二百五十二回

文章目录 概念介绍实现方法示例代码 我们在上一章回中介绍了如何在页面中添加图片相关的内容&#xff0c;本章回中将介绍如何给组件添加阴影.闲话休提&#xff0c;让我们一起Talk Flutter吧。 概念介绍 我们在本章回中介绍的阴影类似影子&#xff0c;只是它不像影子那么明显&a…...

Leetcode 3701 · Find Nearest Right Node in Binary Tree (遍历和BFS好题)

3701 Find Nearest Right Node in Binary TreePRE Algorithms This topic is a pre-release topic. If you encounter any problems, please contact us via “Problem Correction”, and we will upgrade your account to VIP as a thank you. Description Given a binary t…...

网站被攻击了,接入CDN对比直接使用高防服务器有哪些优势

网站是互联网行业中经常被攻击的目标之一。攻击是许多站长最害怕遇到的情况。当用户访问一个网站&#xff0c;页面半天打不开&#xff0c;响应缓慢&#xff0c;或者直接打不开&#xff0c;多半是会直接走开&#xff0c;而不是等待继续等待相应。针对网站攻击的防护&#xff0c;…...

location常用属性和方法

目录 Location 对象 Location 对象属性 Location 对象方法 location.assign() location.replace() location.reload() Location 对象 Location 对象包含有关当前 URL 的信息。Location 对象是 Window 对象的一个部分&#xff0c;可通过 window.location 属性来访问。 L…...

二分图

目录 二分图 染色法判定二分图 匈牙利算法 二分图 二分图&#xff0c;又叫二部图&#xff0c;将所有点分成两个集合&#xff0c;使得所有边只出现在集合之间的点之间&#xff0c;而集合内部的点之间没有边。二分图当且仅当图中没有奇数环。只要图中环的边数没奇数个数的&am…...

[VUE]3-路由

目录 路由 Vue-Router1、Vue-Router 介绍2、路由配置3、嵌套路由3.1、简介3.2、实现步骤3.3、⭐注意事项 4、⭐router-view标签详解 ​&#x1f343;作者介绍&#xff1a;双非本科大三网络工程专业在读&#xff0c;阿里云专家博主&#xff0c;专注于Java领域学习&#xff0c;擅…...

Kafka(六)消费者

目录 Kafka消费者1 配置消费者bootstrap.serversgroup.idkey.deserializervalue.deserializergroup.instance.idfetch.min.bytes1fetch.max.wait.msfetch.max.bytes57671680 (55 mebibytes)max.poll.record500max.partition.fetch.bytessession.timeout.ms45000 (45 seconds)he…...

RK3399平台入门到精通系列讲解(实验篇)共享工作队列的使用

🚀返回总目录 文章目录 一、工作队列相关接口函数1.1、初始化函数1.2、调度/取消调度工作队列函数二、信号驱动 IO 实验源码2.1、Makefile2.2、驱动部分代码工作队列是实现中断下半部分的机制之一,是一种用于管理任务的数据结构或机制。它通常用于多线程,多进程或分布式系统…...

STM32 基于 MPU6050 的飞行器姿态控制设计与实现

基于STM32的MPU6050姿态控制设计是无人机、飞行器等飞行器件开发中的核心技术之一。在本文中&#xff0c;我们将介绍如何利用STM32和MPU6050实现飞行器的姿态控制&#xff0c;并提供相应的代码示例。 1. 硬件连接及库配置 首先&#xff0c;我们需要将MPU6050连接到STM32微控制…...

大数据平台Bug Bash大扫除最佳实践

一、背景 随着越来越多的"新人"在日常工作以及大促备战中担当大任&#xff0c;我们发现仅了解自身系统业务已不能满足日常系统开发运维需求。为此&#xff0c;大数据平台部门组织了一次Bug Bash活动&#xff0c;既能提升自己对兄弟产品的理解和使用&#xff0c;又能…...

JavaScript 中的数组过滤

在构建动态和交互式程序时&#xff0c;您可能需要添加一些交互式功能。例如&#xff0c;用户单击按钮以筛选一长串项目。 您可能还需要处理大量数据&#xff0c;以仅返回与指定条件匹配的项目。 在本文中&#xff0c;您将学习如何使用两种主要方法在 JavaScript 中过滤数组。…...

随机森林(Random Forest)

随机森林&#xff08;Random Forest&#xff09;是一种集成学习方法&#xff0c;通过组合多个决策树来提高模型的性能和鲁棒性。随机森林在每个决策树的训练过程中引入了随机性&#xff0c;包括对样本和特征的随机选择&#xff0c;以提高模型的泛化能力。以下是随机森林的基本原…...

本地引入Element UI后导致图标显示异常

引入方式 npm 安装 推荐使用 npm 的方式安装&#xff0c;它能更好地和 webpack 打包工具配合使用。 npm i element-ui -SCDN 目前可以通过 unpkg.com/element-ui 获取到最新版本的资源&#xff0c;在页面上引入 js 和 css 文件即可开始使用。 <!-- 引入样式 --> <…...

UE5.1_UMG序列帧动画制作

UE5.1_UMG序列帧动画制作 UMG序列帧动画制作相对比较简单&#xff0c;不像视频帧需要创建媒体播放器那么复杂&#xff0c;以下简要说明&#xff1a; 1. 事件函数 2. 准备序列帧装入数组 3. 构造调用事件函数 4. 预览 序列帧UMG0105 5. 完成&#xff01;按需配置即可。...

总结HarmonyOS的技术特点

HarmonyOS是华为自主研发的面向全场景的分布式操作系统。它的技术特点主要体现在以下几个方面&#xff1a; 分布式架构&#xff1a;HarmonyOS采用了分布式架构设计&#xff0c;通过组件化和小型化等方法&#xff0c;支持多种终端设备按需弹性部署&#xff0c;能够适配不同类别的…...

从0到1入门C++编程——04 类和对象之封装、构造函数、析构函数、this指针、友元

文章目录 一、封装二、项目文件拆分三、构造函数和析构函数1.构造函数的分类及调用2.拷贝函数调用时机3.构造函数调用规则4.深拷贝与浅拷贝5.初始化列表6.类对象作为类成员7.静态成员 四、C对象模型和this指针1.类的对象大小计算2.this指针3.空指针访问成员函数4.const修饰成员…...

Robot Operating System 2: Design, Architecture, and Uses In The Wild

Robot Operating System 2: Design, Architecture, and Uses In The Wild (机器人操作系统 2&#xff1a;设计、架构和实际应用) 摘要&#xff1a;随着机器人在广泛的商业用例中的部署&#xff0c;机器人革命的下一章正在顺利进行。即使在无数的应用程序和环境中&#xff0c;也…...

TinyEngine 服务端正式开源啦!!!

背景介绍 TinyEngine 低代码引擎介绍 随着企业对于低代码开发平台的需求日益增长&#xff0c;急需一个通用的解决方案来满足各种低代码平台的开发需求。正是在这种情况下&#xff0c;低代码引擎应运而生。它是一种通用的开发框架&#xff0c;通过对低代码平台系统常用的功能进…...

网页设计与制作web前端设计html+css+js成品。电脑网站制作代开发。vscodeDrea 【企业公司宣传网站(HTML静态网页项目实战)附源码】

网页设计与制作web前端设计htmlcssjs成品。电脑网站制作代开发。vscodeDrea 【企业公司宣传网站&#xff08;HTML静态网页项目实战&#xff09;附源码】 https://www.bilibili.com/video/BV1Hp4y1o7RY/?share_sourcecopy_web&vd_sourced43766e8ddfffd1f1a1165a3e72d7605...

怎样做才能让网站有排名/谷歌浏览器官方正版下载

转文&#xff1a;一个优秀的Java程序员必须了解GC的工作原理、如何优化GC的性能、如何与GC进行有限的交互&#xff0c;有一些应用程序对性能要求较高&#xff0c;例如嵌入式系统、实时系统等&#xff0c;只有全面提升内存的管理效率&#xff0c;才能提高整个应用程序的性能。本…...

罗湖网站建设价格/360推广登录入口官网

一、操作系统配置 1.安装操作系统(NTFS分区)后&#xff0c;装杀毒软件&#xff0c;我选用的是卡巴。 2.安装系统补丁。扫描漏洞全面杀毒 3.删除Windows Server 2003默认共享 首先编写如下内容的批处理文件&#xff1a; echo off net share C$ /del net share D$ /del net …...

做购物比价的网站/网址ip地址查询工具

原文链接&#xff1a;https://cloud.google.com/kubernetes-engine/kubernetes-comic/后台回复“加群”&#xff0c;带你进入高手如云交流群▼喜欢&#xff0c;就给我一个“在看”10T 技术资源大放送&#xff01;包括但不限于&#xff1a;云计算、虚拟化、微服务、大数据、网络…...

Cocos做网站/企业文化案例

2019独角兽企业重金招聘Python工程师标准>>> 如图所示&#xff1a; 解决方法&#xff1a; 1、打开防火墙,点击允许程序通过Window 防火墙 2、点击允许运行另一程序 3、选择安装的IntelliJ IDEA 程序&#xff0c;点击添加 4、然后就会看到 允许的程序和功能栏里 已经…...

wordpress短信插件/池州网络推广

一、免费学术搜索引擎 1.谷歌学术&#xff1a;http://scholar.glgoo.org/ 2.百度学术&#xff1a;http://xueshu.baidu.com/ 3.360学术&#xff1a; 4.读秀&#xff1a;http://www.duxiu.com/ 5.必应&#xff1a; 1&#xff0e;Scholar google&#xff1a;http://scholar.googl…...

站长统计幸福宝2022年排行榜/怎么做网站赚钱

近日中国联通高管在世界5G大会上表示5G套餐不可能持续大幅度降价&#xff0c;原因在于5G建网成本和运营成本实在太高了&#xff0c;然而在实际中至少中国移动和中国联通都已在悄悄推出低至19元的5G套餐。中国移动推的星枫卡已有不少用户获得&#xff0c;该系列套餐最低价只要19…...