悟空活动中台 – 基于 WebP 的图片高性能加载方案
本文首发于vivo互联网技术微信公众号
链接:https://mp.weixin.qq.com/s/rSpWorfNTajtqq_pd7H-nw
作者:悟空中台研发团队
移动端网页的加载速度对用户体验极为重要,是影响页面转化率的关键因素,H5活动页往往使用大量的图片素材来丰富活动效果,素材加载的快慢会对用户感知造成重要的影响。
在《悟空活动中台-H5活动加载优化》一文中我们提到过图片压缩也是提升悟空中台产出H5页面加载性能的重要手段之一,对本篇将从技术选型、架构设计到方案落地,全方位的呈现悟空活动中台基于WebP的图片高性能加载方案。
包含了大量图片素材的H5页面,呈现给用户之前,至少要等待首屏加载完成;要提升加载速度,一方面请求的响应速度要足够快,另一方面要尽量减小传输的数据量。
原始做法是:拿到图片文件后,使用图片压缩工具进行压缩,页面再引入压缩后的小体积文件;
该方案存在严重的问题:效率低下——需要开发者或者设计师针对每张素材图进行手动压缩、肉眼审核质量、压缩得到的文件手动上传。
我们从高清晰度、高压缩比、小体积的诉求出发,最终选择了使用WebP作为首选图片文件格式。
WebP是Google推出的一种同时提供了有损压缩与无损压缩(可逆压缩)的图片文件格式。派生自影像编码格式VP8,被认为是WebM多媒体格式的姊妹项目,是由Google在购买On2Technologies后发展出来,以BSD授权条款发布。
WebP的优势体现在它具有更优的图像数据压缩算法,能带来更小的图片体积,而且拥有肉眼识别无差异的图像质量;同时具备了无损和有损的压缩模式、Alpha透明以及动画的特性,在JPEG和PNG上的转化效果都相当优秀、稳定和统一。
相比于其他相同大小、不同格式的压缩图像,WebP格式的图片拥有更小的体积以及更高的质量,优势十分明显。
下图是一些实测案例:
使用WebP对图片进行有损压缩,在默认配置75%的压缩比下,可以将PNG图片大小压缩至原图体积的13%左右,JPG图片甚至可以压缩至原图体积的10%左右(可参考官方测试页面),实际效果显著。
悟空中台的素材服务架构如下图所示,在nodeserver节点中,我们集成了图片转WebP以及转码后文件存储的服务。
图片压缩服务实现了将用户上传的图片数据,进行格式校验、WebP格式转码、上传文件服务器以及存储的过程。
cwebp是Google官方提供的用于将PNG、JPEG、TIFF或原始Y’CbCr格式的文件压缩转换为WebP格式的命令行编码工具(安装方法请参考官网安装说明)。
使用方法如下:
其中options是压缩参数配置,包含是否启用无损压缩-lossless,压缩系数-q(0~100)等,如使用80的压缩系数对目标文件进行有损压缩:
cwebp-bin模块提供了Node.js使用cwebp能力进行图片压缩转码的接口,我们的图片压缩服务引入该模块模块实现常见格式图片到WebP的转码。
首先需要在服务器执行下述指令以安装模块内部集成的WebP工具程序(libwebp-x.x.tar.gz):
在实际使用时,打包上线时会偶发该安装包资源请求失败的问题;为了安装过程的顺利进行,悟空中台的开发者将该安装包的url由原github下载地址改为了更加稳定的google官方下载地址:
改为
图片压缩服务中使用以下代码调用cwebp工具进行原图到WebP的转码:
通过上文我们了解到,WebP支持有损压缩和无损压缩两种形式,下面我们将针对性的测试两种压缩形式的差异并选出适合的方案。
之所以要对比有损与无损的区别,主要是考虑到时间上的效率和空间上的节约。如果在损失20~30%的精度后,用户的肉眼上难以区分,那么这个精度的损失就是有意义的,因为相对于无损压缩,有损压缩带来的体积的缩小以及压缩时的效率,都比无损压缩更适合用于企业的生产模式下。
我们选取了特点分别为色彩单一、色彩较为丰富和色彩极为丰富的三张图片进行测试:
下面列出了上述图片分别使用WebP无损和有损压缩进行测试的样本数据。
根据上面两份测试数据可以得出,对于同一张图片:
对于不同图片,色彩越丰富,压缩花费的时间越长,压缩比越小;甚至会出现压缩的到的图片体积超过原图的情况(具体原因见下文)。
通过以上测试数据反映的结果来看,有损压缩的优势更大。
使用WebP有损压缩来进行图片的压缩,就不得不考虑接下来的问题:WebP压缩比设置为多少才是最佳实践?
同样的,我们对上述图片进行了以下抽样数据对比:
为什么要拿75%压缩率来做对比?原因是cwebp有损压缩的默认压缩率是75%,这个比例也是通常情况下官方推荐的。
但是在实际业务场景下,75%的压缩比例并不能满足产品需求。比如说一张图片经过压缩后同时在移动端和PC端使用;或图片的色彩空间尤其复杂等等这些情况,再经过75%的有损压缩,我们观察到色彩对比度明显的图片局部有模糊的情况。
经过与设计师同学一起反复的测试实验,我们使用了90%的压缩率来代替默认的75%。此时转换后的图片与原图片结构化差异值SSIM不会小于0.88,视觉效果上用户基本发现不了图片已经进行了压缩。
结构相似性指标(英文:structuralsimilarityindex,SSIMindex)是一种用以衡量两张数位影像相似程度的指标。当两张影像其中一张为无失真影像,另一张为失真后的影像,二者的结构相似性可以看成是失真影像的影像品质衡量指标。相较于传统所使用的影像品质衡量指标,像是峰值信噪比(英语:PSNR),结构相似性在影像品质的衡量上更能符合人眼对影像品质的判断。
关于WebP压缩质量与SSIM的比例关系,请参考Google官方说明WebPCompressionStudy。
我们可以通过在cwebp的执行命令中加入-print_ssim选项,令压缩结果中呈现SSIM信息:
执行输出信息:
我们在测试的过程中还观察到有一些图片转换为WebP格式后得到文件体积比原图更大。经过查阅Google官方文档,得出是由于格式差异以及转码算法导致的:
WebP的压缩率设置超过75%时,在遇到在遇到一些特殊编码的图片时,会调整压缩时的算法,如:
面对这个问题,我们与设计和产品同事共同制定了相应策略:如果压缩后的文件体积大于原图,则使用原图。
在确定合适的压缩比例和压缩方案后,就可以对图片压缩服务进行整体设计,流程如下:
前端页面策略是当网页运行在支持WebP格式的宿主环境(如Chrome、AndroidWebview等)中时,优先使用WebP图片资源,在不支持的宿主环境中,使用原始图片资源。
页面首先需要判断当前宿主环境是否支持WebP:
前面讲解了后台图片压缩和存储服务的设计,接下来我们来一起了解一下前端逻辑上是如何加载WebP图片的。其流程如下图所示:
获取图片url的方式有多种,我们的需求是在图片资源加载前获取真实的图片url,并对其进行处理,而Vue提供的自定义指令可以帮助我们以侵入性极小的形式的拿到目标元素的相关信息。
这里我们使用bind指令进行一次性的初始化设置,在当指令第一次绑定到元素时调用,通过获取到元素关联的素材的url,以img元素为例:
首先判断当前url中是否有素材上传时标记的“nwebp”字样,如果有则说明该图片转为WebP格式后体积反而大于原图,此时无需使用WebP素材替换原有素材;否则,则加载体积更小的WebP文件代替原素材文件。
然后判断当前运行环境是否支持WebP格式图片的渲染,如果支持,则加载WebP素材资源,否则使用原文件链接。
我们在img标签上添加上文定义的v-webp指令如下:
在img元素的create阶段,v-webp指令被bind并执行定义好的hook。
在hook中,我们对于img元素我们可以根据el.src获取到元素关联素材的url,当判断需要采用WebP格式文件时,在原素材url后拼接.webp,从而使得对应图片元素加载的是WebP编码后的素材:
对于运行环境不支持WebP加载的情况,则无需做任何处理,直接加载原图即可:
对于img之外的元素,我们在v-webp指令中传入要作为backgroundImage属性值的url:
在hook中,根据binding.value获取指令的绑定值,即图片url,当判断需要采用WebP格式文件时,在原素材url后拼接“.webp”构造页面用url,否则直接使用原图url,然后为该DOM元素设置内联的backgroundImagestyle即可:
WebP格式虽然优点众多,但是有一个严重的问题——兼容性并不理想。下面我们将从“扩展WebP兼容范围”的诉求出发,探索前端解码WebP文件的可行性。
WebP格式虽然存在压缩率高、体积小等优势,但是其自身并不是通用浏览器图片格式规范,像Safari和FireFox等宿主环境均没有很好的支持该格式(参考自caniuse):
为了保证悟空中台产出的专题页在更多的浏览器中能够以更快的速度加载、渲染,我们又向前走了一步,对WebP格式的纯前端解码做出了下面的探索。
核心理念是将WebP图片作为传输介质,保证了页面图片数据的下载速度;在拿到WebP图片后,对于不支持的宿主环境,将WebP图片进行解码成通用的Base64格式进行渲染。
纯前端是否可以实现WebP格式到Base64格式的解码呢?Google官方团队提供了js解码WebP的库——libwebp.js;但是我们随机挑选一些WebP图片实际测试下来发现性能欠佳:
该方案下WebP图片实际的加载时间为网络数据传输用时+解码用时,面对性能要求较高的场景,WebP的加载速度真要受限于JS不擅长的编解码运算能力了么?当我们再次研究libwebp的资料时,浏览到下述说明:
webp_js还有一个WebAssembly版本。
WebAssembly作为Web标准,在各个浏览器均有较好的支持,兼容性远强于WebP:
WebAssembly可以作为C/C++/Rust等语言的编译目标在浏览器环境中以接近原生的速度运行,计算性能要远远优于JavaScript。
WebAssembly的工作流程如下(图片来自MDN):
其中胶水JS(JS“glue”code)的作用是提供JS调用wasm能力的接口。
我们将libwebp编译成wasm文件供JavaScript调用,提供高速解码WebP的能力。具体的编译过程可以参照libwebp/webp_js的编译说明,编译环境建议使用linux/unix,其余步骤此处不再赘述。
编译后我们得到了wasm文件(gzip压缩后体积51kb)和胶水js(gzip压缩后体积44kb),然后使用上述同样的素材进行性能测试结果如下:
由以上测试基本可以得出:
有了WebAssembly的加持,我们将原有图片加载流程进行了如下图所示升级:
以img元素为例,代码处理逻辑如下:
我们构造了一个图片素材较多的H5专题在Safari中测试,效果如下(为了更好的体现加载过程,下放动图相对实际速度均放慢了3倍):
1、页面元素不添加v-webp指令(加载图片原文件):
2、页面元添加v-webp指令(前端解码WebP):
可以看出在不支持WebP的宿主中,使用了v-webp指令后,页面的响应速度(白屏时间短)和图片渲染速度均有较为明显的提升;至此,我们已经设计并实现了一套相对完善的图片素材加载性能优化方案。
悟空活动中台从提升H5页面图片加载性能的诉求出发,历经:
等一系列手段,探索出一套基于WebP的图片高性能加载方案,更好的赋能了H5活动的开发和运营。悟空中台开发团队将永不止步,持续研究和思考,为大家带来更多的实战技巧,感谢您的阅读。
主题测试文章,只做测试使用。发布者:最新稳定辅助网,转转请注明出处:https://www.744broad.com/15600.html