根据HTTPArchive网站十几年来的对海量网页的统计,网页中加载的图片类资源,体积中位数是1009 KB,请求数量中位数是21个。分别是页面总加载体积2419 KB的41.7% ,和总请求数量72个的29.5%。
可见图片是前端应用绝对的核心内容。
但是长久以来,前端应用的图片类资源面临着许多痛点,主要有:
- 图片体积较大,加载耗时较长。
- CDN流量开销较高:图片类资源数量多、体积大,是CDN费用的主要成本。
前端应用中图片类资源的加载也直接影响着用户体验,图片是首次内容绘制(FCP)和最大内容绘制 (LCP)指标的测量要素,所以从图片类资源入手优化用户体验,对前端应用不可或缺。
1. 图片格式对比#
浏览器平台支持众多图片格式,分别有不同的特性,我们先通过对比了解一下各种格式:
| 格式 | 简介和特性 | 体积示例 (基于后文图片CDN计算) | 发明年份 | 浏览器兼容性 |
|---|---|---|---|---|
jpg | - 最常见、应用最广泛的图片格式 - 体积一般,通常小于 png, gif 等格式 | 158 KB(100%) | 1992 | 几乎所有浏览器都支持 |
png | - 带有透明通道,支持图片部分透明 - 体积较大 | 819 KB(518%) | 1996 | 几乎所有浏览器都支持 |
gif | - 支持自动播放的动态图片 - 体积较大 | 423 KB(267%) | 1989 | 几乎所有浏览器都支持 |
svg | - 矢量图,任意缩放不影响清晰度 - 本质是一种标记语言,可以由浏览器解析渲染 - 体积视内容而定 | – | 2001 | Chrome 4 (2010年1月发布)以上版本支持 参考资料:https://caniuse.com/svg |
webp | - 支持动态图片 - 压缩率较高:支持有损压缩,无损压缩 - 专注于Web平台的表现 - 体积较小 | 136 KB(86%) | 2010 | Chrome 32 (2014年1月发布)以上版本支持 参考资料:https://caniuse.com/webp |
avif | - 支持动态图片 - 压缩率较高 - 体积较小 | 96 KB (60%) | 2019 | Chrome 85(2020年8月发布)以上版本支持 参考资料:https://caniuse.com/avif |
通过上述表格对比不难看出,图片类格式明显分为2大类:
- 传统图片格式:发明于二十年前的
jpg、png、gif、svg等 - 现代图片格式:近十年来发明的
webp、avif等
这两类图片从功能上看:
- 体积:传统格式显著大于现代图片格式。相较于
jpg格式,webp格式的图片一般能减少10%左右的体积,avif格式更是能减少40%以上的文件体积。 - 特性:现代格式都能覆盖传统格式的动态图片、无损压缩等功能和特性,传统格式特性较为单一,现代格式支持动态图片、无损压缩等特性更多。
- 浏览器兼容性:兼容现代格式的浏览器占比相对略少。
如果能使用webp,avif等现代格式,对解决前端应用图片类资源体积较大,加载耗时较长、CDN开销较高的痛点会有明显优化。
但是浏览器兼容性又让我们无法放开手脚,不敢大胆采用现代格式。
所以有什么两全其美的解决方案吗?
2. 自适应选择最优图片格式方案:<picture>元素#
性能和兼容性不可兼得的痛点,Web标准的制定者也早有共识,所以针对性地推出了<picture>元素致力于解决这一痛点。
<picture>元素允许同时引入多个图片格式的子元素,并根据浏览器的兼容性,按先后顺序、自适应加载其中1个格式的图片,实现所有用户根据自身兼容性,获取到最优图片格式。
请看代码示例:
<picture>
<source type="image/avif" srcset="https://cdn.com/image.avif" />
<source type="image/webp" srcset="https://cdn.com/image.webp" />
<img src="https://cdn.com/image.jpg" alt="Example" />
</picture>《
<picture>元素示例》在线运行DEMO:https://jsbin.com/xabenap/edit?html,output
在上述例子中,<picture>元素中包含了2个<source>元素和1个<img>元素。
每个<source>元素都包含一个srcset属性,2处srcset属性分别引用了avif, webp格式的图片URL,type属性则是格式对应的MIME类型。
但是这段HTML只会触发avif一个图片资源的下载。
这是因为,对于<picture>元素,浏览器会按照从上到下的顺序检查<source>元素,加载第一个浏览器兼容格式的图片,并忽略后续的<source>元素和img元素。
例如,如果浏览器对各种图片格式都支持,那么上述例子就只会加载排序最靠上的avif格式图片,不会加载后续的webp和jpg2种格式的图片。
如果浏览器对所有<source>声明的图片格式都不支持,它将降级到加载的<img>元素src属性对应的jpg格式图片,作为兜底方案。
<picture>必须声明一个img子元素,否则如果只有source元素,浏览器不会加载source的src属性对应的图片资源。
也就是说,即使用户的浏览器既不支持解析avif, webp格式的图片,甚至不兼容<picture>元素,浏览器也会自动降级到使用<img>元素,确保始终有正确的图片加载。
因此,建议对<picture>元素中的<img>元素使用jpg或png等兼容性较广泛的图片格式作为src属性值。
注:
<picture>元素的兼容性:
- Chrome 38+(2014年发布,晚于支持
webp,avif格式的版本)- Edge 13+(2015年发布)
- Safari 9.1+(2016年发布)
- IE不支持,会降级为加载
<img>元素参考资料:https://caniuse.com/?search=picture
<picture>元素经过业界大量实践检验,证明可用于生产环境。
例如,Bilibili视频网站,PC版首页的轮播图、封面图都大量使用了<picture>元素:

用上<picture>元素,终于能实现我们图片性能和兼容性两者兼得的需求了,可随之而来的难点是,如何方便高效地生成各种格式的图片,供<picture>元素使用呢?
3. 图片优化神器——图片CDN#
工欲善其事必先利其器,图片CDN就是搭配<picture>元素的神器。
图片CDN是CDN的拓展功能,专用于处理图片类型的各种资源。各大云服务供应商都有提供图片CDN服务,除基础的资源存储外,还附带了多种能力,例如:
- 格式转换、体积压缩
- 尺寸缩放
- 添加水印
这些能力使用起来非常方便,一般只需要修改图片的URL参数,即可实现自动对图片应用相关处理。
以腾讯云CDN的图片处理能力为例,他支持的能力和用法如下:
- 缩放图片:
| 图片类型 | 体积 | URL链接 |
|---|---|---|
| 原图 | 158 KB | sample.jpeg |
| 宽高缩放为原图50% | 46.2 KB | sample.jpeg?imageMogr2/thumbnail/!50p |
| 宽高缩放为指定50*50像素 | 1.8 KB | sample.jpeg?imageMogr2/thumbnail/50x50>! |
- 图片格式转换:
| 图片类型 | 体积 | URL链接 |
|---|---|---|
.jpeg(原图) | 158 KB | sample.jpeg |
.png | 819 KB | sample.jpeg?imageMogr2/format/png |
.webp | 136 KB | sample.jpeg?imageMogr2/format/webp |
.avif | 96 KB | sample.jpeg?imageMogr2/format/avif |
除此之外,还有添加盲水印,高斯模糊,获取图片主色调等众多实用功能,还可以进一步串联使用多种处理方式。
注:各大云服务厂商的图片CDN文档:
- 阿里云:https://help.aliyun.com/document_detail/193596.html
- 腾讯云:图片格式转换文档
- 百度云:https://cloud.baidu.com/doc/BOS/s/ikbitmk26
- AWS:AWS 图片处理文档
使用这些图片CDN,不仅可以让我们方便地使用<picture>元素,还有助于提高日常工作中的开发效率和开发体验,进而让用户获得更好的体验。
例如:
- 利用格式转换能力,搭配上文中介绍的
<picture>元素,实现优先选择加载avif,webp等现代格式图片,减小图片体积、加快加载速度,节省CDN流量。 - 利用尺寸裁剪能力,优化用户头像的加载,在加载头像缩略图时,用50px50px的小尺寸小图;在用户点击放大看大图时,再加载200200px的大图,从而节省流量。
- 利用获取图片主色调能力,为图片添加未加载完成时的背景色,避免页面中出现大片空白,为用户带来更好的浏览体验。
图片CDN辅助函数#
为了便于在前端项目中调用图片CDN的处理能力,我们还可以添加一套辅助函数,例如:
// 预置一批可选格式值,例如缩放到75%,50%,25%
const ImgFormatOptions = {
scaleTo75Percent: '!75p',
scaleTo50Percent: '!50p',
scaleTo25Percent: '!25p',
}
function getImgURL(name, options) {
if (!name) {
return ''
}
// 图片参数例子:?imageMogr2/thumbnail/!50p
let url = `http://examples-1251000004.cos.ap-shanghai.myqcloud.com/${name}`
if (!options) {
return url
}
// 如果参数组合复杂,可以改为使用 URLSearchParams API
// const searchParams = new URLSearchParams()
// searchParams.set(option.key, option.value)
// return `${url}${searchParams.toString()}`
let params = `?imageMogr2/`
if (options.format) {
params+=`format/${options.format}`
} else if (options.thumbnail) {
params+=`thumbnail/${options.thumbnail}`
}
// else if ......
return `${url}${params}`
}基于这套辅助函数,我们就可以在前端的代码逻辑中,方便地使用图片CDN的各项能力,请看代码示例:
// 1. 格式转换
getImgURL('sample.jpeg', {format: 'avif'})
// http://example.myqcloud.com/sample.jpeg?imageMogr2/format/avif
// 2. 裁剪缩略图
getImgURL('sample.jpeg', {thumbnail: ImgFormatOptions.scaleTo50Percent})
// http://example.myqcloud.com/sample.jpeg?imageMogr2/thumbnail/!50p
还可以进一步和前端组件结合使用,封装出加载最优图片格式的<PictureImage>组件,React组件示例代码如下:
const ALL_IMAGE_FORMAT = ['avif', 'webp']; // 按顺序,优先加载靠前的格式
export default function PictureImage({ src, alt }) {
return (
<picture>
{ALL_IMAGE_FORMAT.map((format) => {
return (
<source
srcSet={getImgURL(src, { format })}
type={`image/${format}`}
/>
);
})}
<img alt={alt} src={src} />
</picture>
);
}4. 验证量化和评估#
1. 验证#
1. 使用辅助工具验证图片格式#
在图片CDN开启前,我们可以先在本地用图片格式转化工具,找一些前端应用中的图片,验证格式转换后的体积变化、视觉变化。
例如:
- 使用在线的格式转化工具:https://convertio.co/zh/
- 使用本地安装的 ffmpeg 转化图片格式:
ffmpeg -i input.jpg -c:v libaom-av1 output.avif2. 确认图片CDN开关、收费及限制条件#
图片CDN一般是需要独立开启的CDN服务,使用前记得检查,是否已经对指定的CDN域名开启了图片处理功能。
此外,部分CDN供应商,对特殊的图片处理(例如盲水印)有专门的计费规则,使用前一定确认一下,避免被意外扣费:
最后,图片CDN的处理能力一般都对原图片的尺寸、体积有最大限制,超过这一限制,就不会生效,使用前也应该加以注意,兼容这些边界情况。
2. 量化#
1. 图片类资源加载总体积指标#
我们可以继续复用第5节中介绍的《加载资源总体积指标》,基于Performance API的transferSize或encodedBodySize属性和initiatorType获取图片类资源的体积,并在Grafana中修改查询语句为sum(ResourceSize{type="Image"}),过滤出图片类资源的体积,做出如下可视化图表:

如果,页面和图片数量较多,导致上报数据过多,推荐使用第3节介绍的采样率,在保证量化准确性的基础上,减少上报数据量,节省用户网络流量,降低服务器负担。
2. CDN流量开销#
图片体积相关的优化对CDN的流量开销也会有显著影响,因为图片往往是CDN中占比最大的资源,通过我们的优化体积变化后,预期也会直观的反应在CDN的流量费用上。
我们可以通过CDN供应商的管理后台,查询CDN账单,对比图片格式优化前后的CDN流量费用,用来量化图片优化的效果。
3. FCP和LCP指标#
图片是FCP指标测量的首次渲染内容和LCP指标测量的最大元素的候选项,所以对图片体积相关的优化,也应该使用FCP和LCP作为量化指标。
在生产环境中,更推荐使用我们在第2节介绍的堆叠百分比评分图,稳定、准确地量化这2项指标,避免极端值导致的异常波动。
3. 评估#
通过我们上述介绍的自适应选择最优图片格式优化,预期能让前端应用加载的图片类资源体积显著减少,优化后大多数用户从加载jpg,改为加载webp或avif格式的图片,加载的图片体积预计能减少20%至40%。让图片类资源加载总体积指标在优化后,平均值、最大值、最小值都有显著下降。
同时,CDN流量花费预期也会因为图片体积减少,而有所下降,为我们带来真金白银的优化收益。
此外,如果图片类资源是FCP和LCP指标的测量目标,FCP和LCP的堆叠百分比评分图中,评分为优的占比,预期也会在优化后显著增加,例如下图 LCP评分百分比在11-12图片格式优化上线后,评分为优的占比,有显著的增长,如下图表现:

小结#
这一节中,我们首先了解了各种图片格式的特性和体积表现。
随后,为了解决图片类资源长期以来体积大、加载慢、耗费流量的痛点,学习了用<picture>元素实现自适应选择最优图片格式方案。
以及在开发实践中,使用辅助函数、配套组件方便高效地使用图片CDN的经验。
