掌握srcset与sizes实现响应式图片加载的最佳实践
先看效果,再看代码
我最近在优化一个图片密集型的页面,首屏加载慢得像老牛拉车。F12 一看,好家伙,一张 banner 图居然加载了 3MB 的高清图,而实际显示区域只有 600px 宽。这种浪费带宽的行为,必须干掉。
后来我直接上了 srcset + sizes,效果立竿见影——移动端加载的图片体积直接从 3MB 降到 300KB,而且肉眼几乎看不出画质差异。下面是我亲测有效的写法,建议直接用这种方式:
<img
src="banner-600.jpg"
srcset="banner-600.jpg 600w,
banner-1200.jpg 1200w,
banner-2400.jpg 2400w"
sizes="(max-width: 768px) 100vw,
(max-width: 1024px) 50vw,
33vw"
alt="响应式 Banner"
/>
这段代码的意思是:浏览器根据当前 viewport 宽度和屏幕 DPR(设备像素比),自动选择最合适的图片资源。比如 iPhone 13 Pro Max(DPR=3)在 400px 宽的视口下,会选 1200w 的图(400 * 3 = 1200),而不是傻乎乎地加载 2400w 的图。
这个场景最好用
我在做产品详情页的商品图时,发现不同设备对图片尺寸需求差异很大。桌面端要大图展示细节,移动端则优先加载速度。这时候 srcset + sizes 就特别香。
但要注意,不是所有图片都适合这么搞。如果你的图片是纯装饰性、或者用 CSS background-image 实现的,那还是用媒体查询控制比较好。只有 <img> 标签才支持 srcset 和 sizes。
另外,对于头像、图标这类小图,其实没必要搞多套尺寸——文件体积本身不大,折腾半天收益有限。我一般只对 >100KB 的图片上这套方案。
踩坑提醒:这三点一定注意
我踩过好几次坑,这里重点提醒三个容易翻车的地方:
- 别把
w单位写成px。很多人习惯写600px,但srcset里必须用w(代表图像的实际宽度,单位是 CSS 像素)。写成600px会导致浏览器直接忽略这条规则。 sizes描述的是“图片在页面中占用的布局宽度”,不是“图片本身的宽度”。比如你用 CSS 把图片限制在容器的 50%,那sizes就应该写50vw或者更精确的(min-width: 1024px) 512px, 50vw。我一开始搞反了,结果移动端还是加载了大图。- 永远要有 fallback 的
src。虽然现代浏览器基本都支持srcset,但万一遇到老旧浏览器(比如某些企业内网环境),没有src就直接显示空白。保险起见,src放个中等尺寸的图就行。
高级技巧:配合 picture 标签玩点花的
有时候光靠 srcset 不够用,比如你要根据屏幕方向(横屏/竖屏)或者网络状态切换图片格式。这时候就得上 <picture> 了。
我试过用 WebP 格式进一步压缩体积,效果不错。但 Safari 早期版本不支持 WebP,所以得兜底 JPEG。代码长这样:
<picture>
<source
media="(max-width: 768px)"
srcset="mobile-banner.webp"
type="image/webp"
/>
<source
media="(max-width: 768px)"
srcset="mobile-banner.jpg"
type="image/jpeg"
/>
<source
srcset="desktop-banner.webp"
type="image/webp"
/>
<img
src="desktop-banner.jpg"
alt="Banner"
width="1200"
height="600"
/>
</picture>
这里注意,<picture> 是“匹配即停止”的逻辑,所以要把更具体的条件(比如 max-width: 768px)放前面。另外,type 属性必须写,否则浏览器不知道你给的是什么格式。
不过说实话,现在大部分项目我直接用 srcset 就够了。<picture> 虽然灵活,但维护成本高,生成多套图片也麻烦。除非客户特别要求,否则我一般不主动上。
自动化生成?交给构建工具吧
手动切图、写 srcset 列表太痛苦了,尤其是设计师一天改八遍图。我后来在项目里加了个脚本,用 Sharp(Node.js 图片处理库)自动按比例生成多尺寸图,再动态拼出 srcset 字符串。
比如在 Next.js 里,我写了个 helper 函数:
function generateSrcSet(baseName, widths = [600, 1200, 2400]) {
return widths
.map(w => /images/${baseName}-${w}.jpg ${w}w)
.join(', ');
}
// 使用
const srcSet = generateSrcSet('product-hero');
如果你用的是静态站点生成器(比如 Hugo、Jekyll),也有现成的插件能自动处理。总之,别手动生成,那是在浪费生命。
不过有个小问题一直没完美解决:当图片比例不固定时(比如用户上传的图),自动生成的尺寸可能裁剪不合理。我的临时方案是强制统一比例,虽然损失了部分灵活性,但至少保证了显示效果。如果你有更好的办法,欢迎评论区交流。
最后说两句
srcset 和 sizes 看起来简单,但细节很多。我折腾了两三天才调出满意的效果,中间还被产品经理问“为什么手机上图变模糊了”(其实是 sizes 写错了,加载了过小的图)。
但一旦配对,收益巨大——不仅省流量,还能提升 LCP(最大内容绘制)指标,对 SEO 也有帮助。Google PageSpeed Insights 经常因为这个给你加分。
以上是我踩坑后的总结,希望对你有帮助。这个技术的拓展用法还有很多(比如结合懒加载、CDN 优化),后续会继续分享这类博客。有更优的实现方式欢迎评论区交流。

暂无评论