前端于我
image / 优化

图片优化实践

在web被设计之初,其仅被设计用来承载文本与图片,且在当下,基本没有哪个网站不存在图片。所以对于图片的优化则越发重要,而且由于其优化成本低,优化效果好,往往也是优化的重头戏。

了解图片资源加载与渲染时机

具体分析请看参考文章:Web图片资源的加载与渲染时机

粗浅的归类一下就是,img标签上的图片不管显不显示都会先被加载,而样式表中的只有当元素在渲染树上才会被加载,需要注意的是display:none;的元素在渲染树上,而其子节点不在,所以给display:none;的元素设置背景图能加载,而其子元素的背景图不会加载。

另一个点则是加载时机,img标签上的图片资源在dom树解析时解析到img标签就会发出请求,而样式表上的图片资源则需要在合并渲染树时才会发出,时机上样式表的背景图加载会慢一些。

所以如果是比较重要的首图,建议还是放在img标签加载,或者使用 Resource Hints 进行加载。

各种图片类型选择

日常工作中,大部分人用的最多的图片类型应该还是jpg与png。那其他图片格式都有哪些,其性能怎么样?这里头的门道还是值得说道的。

jpg

jpg图片的特点是“有损压缩,体积小,加载快,不支持透明,没有兼容性问题”。

在日常使用中,大部分的网页大图,轮播图等都会使用jpg,就是因为它的质量可以压缩到比较小但是图片内容(质量)又不会丢失太多。

但是缺点也只是它的压缩是有损压缩,压榨的过于厉害便会变得一塌糊涂。而且还不支持透明。

jpg

这是我随手截的一张图,可以看到即使是截屏的图片压缩都减少了4k左右,更别说设计师们直接从ps导出的图片了。而在双倍图(不会有人用一倍图吧,不会吧,不会吧)的展示效果下,肉眼几乎看不出来有什么差别。

所以拿到切图,先走一遍压缩再说。这可以说是一个最简单可靠有效的优化手段了。

png

png图片的特点是"体积大,无损压缩,质量高,支持透明"。

png图片又有分png-8与png-24,这两者的区别在于支持的颜色数量不同,png-8支持256种颜色,而png-24则支持1600万种颜色。

相应的,自然是支持的色彩越丰富,其质量越大。所以如果你不在乎图片size,一心追求显示效果的话,选png24,否则就是png-8。

相对比jpg,它的优点在于质量高,支持透明。而缺点也很明显,就是这要命的体积。

jpg-png

上图中的png图片就是使用jpg图片在线转的,可以看到png的质量是jpg的近三倍,所以png并不适用于大图,它的最佳应用场景应该是在logo与icon上。

svg

svg是一种基于xml的图片格式。它具有“不失真,体积小,可编程,文本传输,兼容好”的特点。

由于svg是基于xml语言的一种对于图形进行描述的语言,所以它很难被应用于复杂的图形(由于编程成本,渲染成本高)。而也正式因为其是对于图形的描述,所以在图片放大与缩小之后并不会导致图片模糊失真,且体积小。

而其文本传输的特性使其可以随html文档一起被加载,而代价仅仅是html文档多几k的内容(常见的在1~5k左右)

png-svg

上图是两个常用的icon, 一个是png(1.2k),一个是svg(1k)。可以看到在小的,且简单的icon上面,svg的体积更小。而从网络请求来看,svg因为内嵌到了html文档里,所以根本不会发出请求,对于页面引用一个图片,svg在首屏渲染方面有天然的优势。

当dom渲染时,svg就被渲染在页面上了,而图片则是需要等待其加载完成之后才被渲染出来。

svg渲染

上图可以看到,很明显用户还是可以看到png图片闪动的画面。

svg的缺点也很明显,其可编程也说明了拥有其他图片格式没有的学习成本,如果需要手动改动一个icon,还是比较麻烦的(虽然一般都是叫设计师重新输出一张)

其次则是复杂图片的图片体积会直线上升,因为它是文本传输的图片格式,所以在压缩方便并没有什么优化空间,对比其他图片类型就相形见拙了。

svg2

比如上图,同一张图片,png的size是3.1k, 而svg达到了6k。

还有一个缺点则是它的渲染成本是比其他图片类型要高的,这决定了它不能在页面上大量的使用(降低渲染损耗)。

所以svg的应用场景应在“简单的,少量的,清晰度高的”icon或logo。

base64

base64是一种编码格式,而非一种图片格式。它的特点是“文本传输,依赖编码,小图标解决方案”

Base64 是一种用于传输 8Bit 字节码的编码方式,通过对图片进行 Base64 编码,我们可以直接将编码结果写入 HTML 或者写入 CSS,从而减少 HTTP 请求的次数。

前面说道svg在处理简单的小图标方面很优越,但是在复杂的图片则不理想,这时候就轮到base64出场了。

svg还能单独抽离出来作为.svg后缀的文件,而base64则只能内嵌在代码中。

base64

可以看到,还是原来那张图,这次通过png/svg/base64对比,经过gzip压缩之后的png与base64大小一致(2.9k),甩svg一条大街(6k多)。

而且base64不需要加载,它跟svg一样可以随文档一起加载,这不仅仅是省去了图片加载时间的问题,要知道图片加载的时候是需要占用一个链接的,而有识之士都知道,浏览器对于同域名下的请求并发数是有限制的,如果你的所有资源都是放在同一个域名下的话,此时你占用一个链接,就相当于把其他等待中的请求再往后推了。

既然base64这么优秀,要不干脆把所有图片都转成base64?

这是不现实的,少量的首屏icon转成base64可以加快页面加载,而过多的html却会减慢主文档的加载速度,主文档慢了就更不用提什么首屏优化了。况且图片转成base64后大小会膨胀到原来的4/3左右,如果把大图转成base64那它会更大。这也是为什么base64只适合用来做复杂一些的小图标的原因。(简单的还是建议用svg,它的体积会更小。)

值得高兴的是webpack的url-loader具备基本的小图标转化base64的能力。

webp

webp是最近几年出现的新型图片格式,它的特点是“年轻,体积小,兼容性不好“。

谷歌声称,在同等质量的情况下,WebP 格式比 JPEG 体积小25 - 34%。先来一张对比。

webp

可以看到原图是178k的jpg, 转成webp之后仅仅才18.5k。直接十分之一的质量,但是展示效果肉眼同样是看不出来,你馋不馋我不知道,反正我是馋了。

并且它还支持透明,有损压缩,无损压缩。简直就是图片界的超新星啊。

既然这么牛,为什么当前还是jpg,png居多呢?

新的东西往往兼容性不好,这是互联网的共识了。webp也是一样的。

webp兼容性

兼容性差主要是体现在ie与safari。在现在苹果手机市场占有率仍然非常客观的情况下,图片无法显示是无法接受的。

而为什么有的大厂又可以用webp图片呢?

因为大厂做了兼容呀,它们都会自己建一个图片转换服务,通过url参数以及请求的User-Agent去判断是否给你返回webp格式的图片(甚至是压缩质量,图片大小)。而小公司往往没那么多考虑。

所以,如果有条件的话,务必使用webp。

图片加载优化手段

雪碧图(CSS 精灵、CSS Sprites)

雪碧图是一位老同学了,它的意义就在于将一些散碎的小图标合并成一张大图,进而减少请求数,一般就是应用在多个小图标加载以及序列帧图片合并。

图片懒加载

图片懒加载的实现原理是将图片的src记录在一个位置而不是直接设置进src属性(一般直接设置到元素的data-src内),当判断元素曝光或者即将曝光时【一般运用intersectionObserver或者scroll事件与getBoundingClientRect()计算实现】,才将src设置进去,此时才会发出资源请求。

图片预加载

页面首屏的图片资源应该尽快加载,非首屏的图片资源应该在请求线程空闲的时候静默加载重要图片(如轮播图中懒加载的图片)。

这一方面可以结合Resource Hints进行预加载。

总结

  1. 首屏简单小图标用 svg内嵌,首屏复杂小图标用 base64内嵌(注意文档大小,做好取舍)。

  2. 使用图片前先压缩一下。

  3. 非首屏小图标可以根据功能进行雪碧图合并。

  4. 合理使用Resource Hints控制图片加载时机(注意样式表内的图片加载时机)。

  5. 能用webp就用webp。

  6. 服务端渲染,或者长列表图片,尽量使用图片懒加载控制图片加载时机(不要跟首屏抢资源)。

参考文档

Web图片资源的加载与渲染时机

前端性能优化原理与实践 - 图片优化篇

发表于: 2020-07-22