[翻译]优化 web 页面上的图片加载过程
原文链接:blog.cloudflare.com/optimizing-…
很多网络页面内容是图片。在 web 页面的加载过程中,图片的加载占到了整体加载字节的 51%,所以图片加载过程中任何针对加载速度或者图片大小的优化都对页面性能有显著影响。
今天,我们发布了一款新工具:Cloudflare 的图片优化测试工具。只要将网站的 url 输入,我们就会自动进行一系列测试来确定当前页面是否存在进行任何优化操作来为用户提供更好的图片。

用户如何感知加载速度
每个浏览网页的人都曾经遇到过加载速度非常慢的网页。通常情况下,这说明当前页面上的图片未经过优化。对于目标网页而言,它们要不过大,要么就是嵌入到当前页面上时不能提供足够的信息。
页面上的图片随着像素从上至下填充可能要经过一段很长的加载时间,或者更糟糕,随着浏览器逐渐获知它们的大小,它们可能会引起重绘从而造成页面布局的巨大变化。这些问题一直困扰用户,直到 2021 年 8 月,搜索引擎开始根据这些指标为页面打分。
可以理解的是,缓慢的页面加载速度会影响页面的“回弹率”。页面的“回弹率”指的是从当前页面快速离开的访问者的比率。在电商页面尤其是这样,回弹率对经济收入有直接的影响。电商页面通常有很多图片,这个时候,优化页面上的图片以减少加载负荷和服务器流出负荷就是很重要的,这样可以提升网页在搜索引擎上的排名,最终提供给用户良好体验。
测量速度
图片大小
对于开发者来说,他们所能做的对加载性能提升最大的一个操作就是确保图片都有合适的大小。现代照相机拍的图片非常大,一些先进的旗舰手机的传感器要求像素很大。以 Samsung Galaxy S21 Ultra 为例,这款手机有 108MP 的传感器,能够捕获 12000 乘 9000 的像素图片。然而这款手机的屏幕宽度仅有1440 像素。在物理上不可能将每个像素都展示在该设备上,对于一个风景图片,仅展示 12% 的像素。
在网页上嵌入图片会有同样的问题,但是这次该图片中无用像素都要通过 Internet 传输。最终造成了服务器的无必要加载、高输出代价以及对访问者来说更长的加载时间。当访问者通过移动设备访问时情况更糟糕,因为移动端会产生更慢的链接以及更有限的数据使用。在快速的 3G 链接情况下,108 mp 的图片会消耗 26 mb 的用户数据空间以及网站流出带宽,要花费两分多钟去加载。
通过使用最高分辨率的图像来避免像素化的图片或阻塞是危险的操作,但当重绘操作正确执行的时候,它就不是问题了。阻塞行为通常发生在一个图片被处理很多次的时候(比如,用户在一个会压缩图片的平台上将该图片重复上传和下载)。像素化的图片是因为图片被压缩至小于当前屏幕的大小。
所以,作为 web 开发者如何避免这些陷阱并且确保正确大小的图片被传送给访问者的设备呢?这里有两种主要方法
- 使用
srcset和sizes监测设备情况
当在网页上嵌入一个图片的时候,开发者通常简单传入 src 属性给 img 标签:
<img src="hello_world_12000.jpg" alt="Hello, world!" />
自从 2017 年,所有的现代浏览器都支持了更加动态的 srcset 属性。这一属性允许开发者传入多个源,具体使用哪个则取决于用户的浏览器情况。
<img srcset="hello_world_1500.jpg 1500w,
hello_world_2000.jpg 2000w,
hello_world_12000.jpg 12000w"
sizes="(max-width: 1500px) 1500px,
(max-width: 2000px) 2000px,
12000px"
src="hello_world_12000.jpg"
alt="Hello, world!" />
在这里使用 srcset 通知浏览器当前的图片有三种大小,每种都有具体的不同的宽度,1500 像素、2000 像素和原始的 12000 像素。然后,浏览器通过衡量 sizes 属性( (max-width: 1500px) 和 (max-width: 2000px))指定的媒体标签情况来选择 srcset 属性中合适大小的图片。如果浏览器的视窗窗口宽度小于 1500 像素,那么就会加载 hello_world_1500.jpg,如果窗口宽度在 1500 像素和 2000 像素之间,那么就会加载 hello_world_2000.jpg,如果窗口宽度大于 2000 像素,那么浏览器最终会加载 hello_world_12000.jpg。
picture 标签也可以使用能够支持多个选择器的 source 子元素来完成类似功能。
- Client Hints
有些浏览器实现了 Client Hints 标准,有些则没有。这一标准规定了一系列 HTTP 请求头,这些请求头可以将客户端的设备信息告知服务器。例如,浏览器可以在请求一个图片的时候附加 Viewport-Width 头信息将浏览器视窗大小告知服务器。(注:当前属性正在重命名到 Sec-CH-Viewport-Width) 的过程中)
这一属性极大地简化了之前示例中的标记,事实上,在原始的 html 中没有什么需要改变的:
<img src="hello_world_12000.jpg" alt="Hello, world!" />
如果 Client Hints 该属性被支持,那么当浏览器发出加载 hello_world_12000.jpg 的请求的时候,该请求很有可能会带上如下请求头:
Viewport-Width: 1440
即使请求一开始请求的图片是 hello_world_12000.jpg,服务器也可以自动的选择一个相对较小的图片(例如: hello_world_1500.jpg)。
通过使浏览器请求合适大小的图片资源,我们同时为服务器和用户节省了带宽和加载时间。
格式
图片格式有很多,包括 JPEG,PNG,GIF,WebP 以及最新的 AVIF。AVIF 是最新的图片格式,已经被广泛业界标准所支持,它比之前的格式表现得要好。AVIF 支持 alpha 信道的透明传输、支持动画格式并且相比同等内容的 JPEG 格式图片要小 50% 左右(WebP 的大小减少仅仅30%)。
在 Google 的 Chrome 支持该格式之后,我们在去年将 AVIF 格式添加到了 Cloudflare 的图片缩放工具中。FireFox 93 版本(将在2021 年 10 月 5 日正式发布)将会是火狐浏览器的首个支持 AVIF 格式的版本,与此同时,微软和苹果公司也成为了 AVIF 格式的开放媒体联盟的一员,我们希望很快就能看到 Edge 和 Safari 对其的支持。在这些现代格式出现之前,我们同样有创新的方法来提升页面上图片的加载性能。BlurHash 就是一种将非常小的图片表示嵌入 html 标记中的技术,该技术使得页面能够立即渲染并且充当图片的占位符。这一 hash 表示法产生了与最终图片的颜色相似的混淆图片从而减轻了加载体验中的阻塞感。
渐进式 JPEG 图片在实现效果上与之前类似,但它是图片格式本身的内置属性。在这种图片中,图片不是从上到下以图片字节为单位加载的,而是将字节组织成了表示图片细节的不同图层。这一格式产生了更好的加载体验,用户第一次看到的是低质量的图片,之后随着字节加载,图片逐渐优化。

质量
不像它们的前辈 JPEG 格式一样,更新一点的图片格式,类似 WebP 和 AVIF 支持无损压缩。对于一些情况,无损压缩更合适,但是对于大多数的网页,速度才是最优先被考虑的因素,因此在可以节省时间和字节的情况下,我们通常可以接受图片质量上的微小损失。
决定使用何种质量的图片需要权衡:低质量的图片会使得骨架在图片上展示,太高质量的图片又会使得图片变得非常大。Butteraugli 和 SSIM 提供了示例算法,这些算法能够估算我们对于不通图片质量的感受,但是这一过程很难自动化,所以因此最好手动设置。总而言之,我们发现 85% 的压缩比例是最优的设置。
标签
之前的所有技术旨在减少图片使用的字节数。这一办法对提升图片加载速度和 LCP(Largest Contentful Paint)指标有极大的好处。然而,为了提升 CLS(Cumulative Layout Shift)指标,我们必须尽量减少页面重绘的次数,我们可以通过提前告知浏览器图片大小来做到这一点。
在一个鲜少优化的页面,嵌入图片的标签中不会指明图片的大小。浏览器获取这些图片,然后只有在收到头部字节的时候才能确定图片的大小。这一现象表现为,浏览器在未收到任何字节的时候渲染页面,之后再确定图片大小的时候重新渲染一次页面。这对用户来说是很奇怪的,容易影响使用。
在 HTML 的 IMAGE 标签中写明图片的大小从而允许浏览器为图片在开始加载之前分配空间是很重要的。甚至我们可以通过设置大小动态的加载响应式图片:假定纵横比是恒定的,通过告知浏览器原始图片的宽度和高度,我们就可以自动计算正确的高度,即使是在使用宽度选择器的时候也可以。
<img height="9000"
width="12000"
srcset="hello_world_1500.jpg 1500w,
hello_world_2000.jpg 2000w,
hello_world_12000.jpg 12000w"
sizes="(max-width: 1500px) 1500px,
(max-width: 2000px) 2000px,
12000px"
src="hello_world_12000.jpg"
alt="Hello, world!" />
最后,懒加载技术能够减少浏览器需要在页面加载开始之前做的工作。通过将图片加载过程推迟到需要的时候,浏览器能够优先处理更加重要的资源,例如字体、样式以及 JavaScript。通过将图片上的 loading 属性设置为 lazy,开发者可以告诉浏览器在该图片进入视窗时才加载它。例如,在一个渲染一个产品格的电商页面上,使用这一技术意味着对用户而言页面加载的更快了,而且随着用户向下滚动,平滑地获取下方即将展示的图片。这一属性目前被除了 safari 的所有浏览器支持,它在 Safari 上目前只是实验性的特性。
<img loading="lazy" … />
Hosting
最后,你可以通过将页面的图片一起放在同一个第一方域名下来提升图片加载体验。如果每个图片都放在不同的域,浏览器将会对每张图片执行 dns 查找,创建 tcp 链接然后执行 tls 握手协议。当他们存储在同一个域名中,尤其是当域名就是页面本身的时候,浏览器将会复用链接,从而提升图片加载速度。
测试网站
今天我们宣布 Cloudflare 的图片优化测试工具发布了,简单地将网页 url 输入,我们会自动进行一些列测试来确定当前页面上是否有任何可能进行操作来提升图片加载体验。
我们使用 WebPageTest 和 Lighthouse 来计算核心网络指标在两种版本页面上:一个是原始页面,一个是经过 cloudflare 的自动化优化的页面。这些优化是使用 clourflare 的工作进程结合我们的图片缩放工具实现的,并且会转化图片的格式、质量和大小。
我们针对页面的关键性能指标进行报告,关键指标包含了之前提到的 cls 和 lcp,同样包括针对页面上每个图片能够进行的优化。
Cloudflare Images
Cloudflare Images能够帮助你解决本文中提到的几个问题。通过将图片存储在 Cloudflare 中并且只是一组参数,我们可以将最优化的图片传递到你的网站或是应用。我们自动地设置最后话的图片格式并且允许你定制适应于你的用例的图片大小。
我们非常开心您能够使用Cloudflare Images,并且在不久的将来就会新增一系列的特性和集成。每月只需要支付6美元就可以使用Cloudflare Images。