如何处理 Web 图片优化?

优化的图片是影响网站性能的主要因素之一,尤其会影响初次加载。取决于图像的分辨率和画质,图片可能占据整个网站流量的 70%.

生产环境出现未优化的图片并显著影响初次加载速度的现象还是挺常见的。缺乏经验的开发者通常没有意识到这一潜在问题,也不了解各种优化图片的工具和方法。

本文的目标是介绍优化 web 图片的主要工具和方法。

计算 JPG 文件尺寸

未压缩图片的尺寸很容易计算,只需将图片的长宽相乘(px 值),再乘以 3 字节(因为 RGB 色彩系统使用 24 个位元)。所得结果除以 1,048,576(1024 * 1024)即得到兆字节。

image_size = (image_width * image_height * 3) / 1048576

比如,计算分辨率为 1366px x 768px 的未压缩图片的大小:

1366 * 768 * 3 / 1048576 = 3Mb

现在网站的尺寸平均在 2Mb 和 3Mb 之间,想象一下,一张未压缩的图片就占掉了 80% 的流量。在网速较慢的移动网络上,3Mb 大小的图片要花很久才能加载完毕。如果等待网站加载的用户大部分时间花在等待单张图片加载,那网站会损失不少流量。想想就可怕,是吗?

所以,在保证图片分辨率和画质可接受的前提下,我们可以做什么来优化下图片呢?

在线图片优化

如果你的项目是一个简单的静态网站,只有少量不经常变动(甚至从来不会变动)的图片,那么你可以直接使用在线工具。这些工具使用各种算法压缩图像,效果很不错,对简单项目而言完全够用。

就我个人所知,比较著名的在线工具有:

  • Compressor.io,支持 JPG、PNG、SVG、GIF,每次上传 1 个文件
  • Squoosh,支持 JPG、PNG、SVG、GIF,每次上传 1 个文件
  • Optimizilla,支持 JPG、PNG,最多每次上传 20 个文件
  • TinyPNG,支持 JPG、PNG,最多每次上传 20 个文件
  • SVGMinify,支持 SVG,每次上传 1 个文件
  • svgomg,支持 SVG,每次上传 1 个文件

自动化解决方案

然而,如果你做的是多人协作的复杂项目,使用大量图片,在加入每张图片时都手动操作一下很乏味。同时,还存在由于人为错误或其他因素导致一些图片没有优化的风险。

复杂项目常常使用同样复杂的构建系统,比如 GulpWebpackParcel。配置一下这类构建系统,加入图片优化插件很方便。这样就可以完全自动化图片优化过程,在项目中加入图片后就可以优化它们。

就我所知,最有名的插件是 imagemin,可以作为命令行工具使用,也可以作为构建工具的插件使用:

图片加载优化

我们前面介绍了如何通过压缩图片降低文件尺寸,但不过多改变图片分辨率和影响画质。尽管优化图片后文件尺寸能降低不少,但一次性加载大量优化过的图片(比如电商网站的商品列表页面)还是会影响性能。

懒加载

懒加载也叫按需加载,意思是仅加载当前视图(用户屏幕显示范围)内的图片,不加载其他图片(直到它们出现在当前视图内时才加载)。

只有较新版本的浏览器才支持原生的懒加载特性,不过有许多基于 JavaScript 的方案。

原生懒加载

  • 原生懒加载
<img src="image.jpg" loading="lazy" alt="Sample image" />
  • 基于 JavaScript 的方案

就我所知,最知名的方案有:

verlok/lazyload
yall.js
Blazy (现在没有维护)

渐进式图片

尽管懒加载在性能方面表现出色,但是用户滚动屏幕后需要盯着空白区域等待图片加载,这样的用户体验不太好。网速慢的情况下,下载图片会非常慢。所以我们还需要渐进式图片。

渐进式图片的意思是在高画质图像加载完之前会先显示低画质版本。低画质版本由于画质低、压缩率高,尺寸很小,加载很快。在两者之间我们也可以根据需要显示不同画质的版本。

类似于先加载页面的骨架,渐进式图片这一技术让用户产生图片加载变快的印象。用户不再盯着一片空白区域等待事情发生,而能看到图像变得越来越清晰。
渐进式图片有基于 JavaScript 实现的方案:
progressive-image

响应式图片

我们还需要留意使用尺寸合适的图片。

例如,假设图片在桌面浏览器上显示的最大宽度为 1920px,平板上的最大宽度为 1024px,手机上的最大宽度为 568px,那么最简单的方案是使用 1920px 的图片,这样可以满足所有场景。不过,这种情况下,网速慢、网络不稳定的智能手机用户需要等很久图片才能加载完毕,这就又碰到了我们文章开头提到的问题。

好在我们可以通过 picture 元素告诉浏览器基于媒体查询下载相应的图片。尽管现在 93% 的用户使用的浏览器都支持这一特性,但是这个元素内部还是包含了一个 img 元素,以兼容不支持这一特性的浏览器。

<picture>  <source media="(min-width: 1025px)" srcset="image_desktop.jpg">  <source media="(min-width: 769px)" srcset="image_tablet.jpg">  <img src="image_mobile.jpg" alt="Sample image"></picture>

使用 CDN

Cloudinary、Cloudflare 之类的 CDN 服务可以在服务器上优化图片,将优化后的图片传送给用户。如果你的站点使用 CDN,可以看下静态资源优化选项。这样我们就不用操心图片优化,由 CDN 在服务端完成优化。我们只需要操心懒加载、渐进式图片等前端的加载方案。

WebP 图像格式

WebP 是由 Google 开发的专为 web 优化的图像格式。根据 canIUse 的数据,大部分用户使用的浏览器支持 WebP 格式。另外使用 picture 元素也可以很方便地兼容不支持 WebP 的浏览器。

<picture>  <source type="image/webp" srcset="image.webp" />  <source srcset="image.jpg" />  <img src="image.jpg" alt="Sample image" /></picture>

有很多在线文件格式转换工具可以把图片转为 WebP 格式,不过 CDN 服务可以在服务端完成这一格式转化。

为高分屏优化

考虑高分屏很有必要,不过这个更多的是用户体验优化。

例如,假定我们在 768px 的屏幕上显示一张 768px x 320px 的图片。但是屏幕有 2x 的密度,也就是说屏幕宽度实际是 2 x 768 = 1536 px。这就意味着我们将 768 px 的图片拉升到 1536 px,这就导致高分屏上的图片看起来很模糊。

为了解决这一问题,我们需要提供为高分屏优化的图片。我们需要单独创建相当于普通屏幕 2 倍或 3 倍分辨率的图片,然后在 srcset 属性上使用 2x 标签表明这是为高分屏准备的图片。

<img src="image-1x.jpg" srcset="image-2x.jpg 2x" alt="Sample image" />

例子

支持高分屏的响应式 WebP/PNG 图片:

<picture>    <source srcset="./images/webp/hero-image-420-min.webp 1x, ./images/webp/hero-image-760-min.webp 2x" type="image/webp" media="(max-width: 440px)">    <source srcset="./images/minified/hero-image-420-min.png 1x, ./images/minified/hero-image-760-min.png 2x" media="(max-width: 440px)">    <source srcset="./images/webp/hero-image-550-min.webp 1x, ./images/webp/hero-image-960-min.webp 2x" type="image/webp" media="(max-width: 767px)">    <source srcset="./images/minified/hero-image-550-min.png 1x, ./images/minified/hero-image-960-min.png 2x" media="(max-width: 767px)">    <source srcset="./images/webp/hero-image-420-min.webp 1x, ./images/webp/hero-image-760-min.webp 2x" type="image/webp" media="(max-width: 1023px)">    <source srcset="./images/minified/hero-image-420-min.png 1x, ./images/minified/hero-image-760-min.png 2x" media="(max-width: 1023px)">    <source srcset="./images/webp/hero-image-760-min.webp 1x, ./images/webp/hero-image-960-min.webp 2x" type="image/webp" media="(max-width: 1919px)">    <source srcset="./images/minified/hero-image-760-min.png 1x, ./images/minified/hero-image-960-min.png 2x" media="(max-width: 1919px)">    <source srcset="./images/webp/hero-image-960-min.webp" type="image/webp">    <source srcset="./images/minified/hero-image-960-min.png">    <img  src="./images/minified/hero-image-960-min.png" alt="Example"></picture>

结语 —— 优化优先级

  1. 使用优化后的图片(使用自动构建工具、在线服务、CDN 优化)
  2. 使用懒加载(在浏览器有更好的原生支持前考虑使用 JS 方案)
  3. 为高分屏优化图片
  4. 使用 WebP 格式
  5. 使用渐进式图片

可选: 如果条件允许,记得使用 CDN 加速图片(和其他静态资源)。

转载自 New Frontend 网站。

本作品采用《CC 协议》,转载必须注明作者和本文链接
嗨,我是波波。曾经创业,有收获也有损失。我积累了丰富教学与编程经验,期待和你互动和进步! 公众号:上海PHP自学中心 付费知识星球:破解面试:程序员的求职导师
本帖由系统于 4年前 自动加精
《L05 电商实战》
从零开发一个电商项目,功能包括电商后台、商品 & SKU 管理、购物车、订单管理、支付宝支付、微信支付、订单退款流程、优惠券等
《G01 Go 实战入门》
从零开始带你一步步开发一个 Go 博客项目,让你在最短的时间内学会使用 Go 进行编码。项目结构很大程度上参考了 Laravel。
讨论数量: 9
lufeijun1234

厉害

4年前 评论

感谢楼主分享,学到新的标签 picture 和 img 标签的 srcset。 :+1:

4年前 评论
Code_Er

搂主 如果是一个图片站的话 应该如何处理这海量的上传图片优化

4年前 评论
Icy 4年前

oss 阿里云图片裁剪。不建议用webp,因为我的mac发现打不开。不支持webp。

4年前 评论

举手支持,干得漂亮 :+1:

4年前 评论

楼主,有没有研究过如何快速生成图片,比如一些项目中需要生成复杂的海报图片,因为图片上有一些随机的信息,所以不能提前生成,所以只能每次点击再重新生成加载,导致非常慢,有好的解决办法吗?

4年前 评论

不错,很全面。

3年前 评论

tinypng 经常用,压缩图片很实用

3年前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!
司机 @ 欣昊玉
文章
273
粉丝
339
喜欢
558
收藏
1106
排名:64
访问:12.2 万
私信
所有博文
社区赞助商