Web开发

首页 » 常识 » 诊断 » 程序员,一文掌握Web应用中的图片优化
TUhjnbcbe - 2023/7/9 2:57:00

作者

fecoder

责编

郭芮

这篇文章,我们将一起探讨,Web应用中能对图片进行什么样的优化,以及反思一些“负优化”手段。

为什么要对图片进行优化?

对于大多数前端工程师来说,图片就是UI设计师(或者自己)切好的图,你要做的只是把图片丢进项目中,然后用以链接的方式呈现在页面上,而且我们也经常把精力放在项目的打包优化构建上,如何分包,如何抽取第三方库........有时我们会忘了,图片才是一个网站最大头的那块加载资源(见下图),虽然图片加载可以不不阻碍页面渲染,但优化图片,绝对可以让网站的体验提升一个档次。

从图片大小开始优化

压缩图片可以使用统一的压缩工具——imagemin,它是一款可以集成多个压缩库的工具,支持jpg、png、webp等等格式的图片压缩,比如pngquant、mozjpeg等等。作为测试用途,我们可以直接安装imagemin-pngquant来尝试png图片的压缩:

PNG压缩

npminstallimageminnpminstallimagemin-pngquant

这里先安装imagemin库,再安装对应的png压缩库。

constimagemin=require(imagemin);constimageminPngquant=require(imagemin-pngquant);(async()={awaitimagemin([images/*.png],build/images,{plugins:[imageminPngquant({quality:65-80})]});console.log(Imagesoptimized);})();

我们可以在quailty一项决定压缩比率,65-80貌似是一个在压缩率和质量之间实现平衡的数值,腾讯AlloyTeam出品的gka图片处理工具,同样使用到了imagemin库,他们默认也是使用65-80的选项:gka代码。用它压缩一张png图片,我们看看效果如何:

这是压缩前的:

这是压缩后的:

从肉眼上几乎看不出区别,但实际上减少了百分之77的体积!读者可以自己保存图片进行比较。

JPG/JPEG压缩与渐进式图片

压缩jpg/jpeg图片的方式与png类似,imagemin提供了两个插件:jpegtrain和mozjpeg供我们使用。一般我们选择mozjpeg,它拥有更丰富的压缩选项:

npminstallimagemin-mozjpeg

constimagemin=require(imagemin);constimageminMozjpeg=require(imagemin-mozjpeg);(async()={awaitimagemin([images/*.jpg],build/images,{use:[imageminMozjpeg({quality:65,progressive:true})]});console.log(Imagesoptimized);})();

注意到我们使用了progressive:true选项,这可以将图片转换为渐进式图片,关于渐进式图片,它允许在加载照片的时候,如果网速比较慢的话,先显示一个类似模糊有点小马赛克的质量比较差的照片,然后慢慢的变为清晰的照片:

而相比之下,非渐进式的图片(BaselineJPEG)则会老老实实地从头到尾去加载:

简单来说,渐进式图片一开始就决定了大小,而不像Baseline图片一样,不断地从上往下加载,从而造成多次回流,但渐进式图片需要消耗CPU去多次计算渲染,这是其主要缺点。当然,交错式png也可以实现相应的效果,但目前pngquant没有实现转换功能,但是ps中导出png时是可以设置为交错式的。

在真实项目中如何操作?

实际项目中,总不能UI丢一个图过来你就跑一遍压缩代码吧?幸好imagemin有对应的Webpack插件,在Webpack遍地使用的今天,我们可以轻松实现批量压缩:

npminstallimagemin-webpack-plugin

先安装imagemin-webpack-plugin:

importImageminPluginfromimagemin-webpack-pluginimportimageminMozjpegfromimagemin-mozjpegmodule.exports={plugins:[newImageminPlugin({plugins:[imageminMozjpeg({quality:,progressive:true})]})]}

接着在Webpack配置文件中,引入自己需要的插件,使用方法完全相同。具体可参考GitHub的文档imagemin-webpack-plugin。

通过图片按需加载减少请求压力

图片按需加载是个老生常谈的话题,传统做法自然是通过监听页面滚动位置,符合条件了再去进行资源加载,我们看看如今还有什么方法可以做到按需加载。

使用强大的IntersectionObserver

IntersectionObserver提供给我们一项能力:可以用来监听元素是否进入了设备的可视区域之内,这意味着我们等待图片元素进入可视区域后,再决定是否加载它,毕竟用户没看到图片前,根本不关心它是否已经加载了。这是Chrome51率先提出和支持的API,而在年的今天,各大浏览器对它的支持度已经有所改善(除了IE,全线崩~):

废话不多说,上代码。首先,假设我们有一个图片列表,它们的src属性我们暂不设置,而用data-src来替代:

liimgclass=list-item-imgalt=loadingdata-src=a.jpg//liliimgclass=list-item-imgalt=loadingdata-src=b.jpg//liliimgclass=list-item-imgalt=loadingdata-src=c.jpg//liliimgclass=list-item-imgalt=loadingdata-src=d.jpg//li

这样会导致图片无法加载,这当然不是我们的目的,我们想做的是,当IntersectionObserver监听到图片元素进入可视区域时,将data-src还给src属性,这样我们就可以实现图片加载了:

constobserver=newIntersectionObserver(function(changes){changes.forEach(function(element,index){//当这个值大于0,说明满足我们的加载条件了,这个值可通过rootMargin手动设置if(element.intersectionRatio0){//放弃监听,防止性能浪费,并加载图片。observer.unobserve(element.target);element.target.src=element.target.dataset.src;}});});functioninitObserver(){constlistItems=document.querySelectorAll(.list-item-img);listItems.forEach(function(item){//对每个list元素进行监听observer.observe(item);});}initObserver();

运行代码并观察控制台的Network,会发现图片随着可视区域的移动而加载,我们的目的达到了。

PS:额外介绍一个vue的图片懒加载组件vue-view-lazy,也是基于IntersectionObserver实现的。

Chrome的黑科技——loading属性

从新版本Chrome(76)开始,已经默认支持一种新的html属性——loading,它包含三种取值:auto、lazy和eager(之前有文章说是lazyload属性,后来chrome的工程师已经将其确定为loading属性,原因是lazyload语义不够明确),我们看看这三种属性有什么不同:

auto:让浏览器自动决定是否进行懒加载,这其中的机制尚不明确。lazy:明确地让浏览器对此图片进行懒加载,即当用户滚动到图片附近时才进行加载,但目前没有具体说明这个“附近”具体是多近。eager:让浏览器立刻加载此图片,也不是此篇文章

1
查看完整版本: 程序员,一文掌握Web应用中的图片优化