掌握srcset与sizes实现响应式图片加载的最佳实践

艺涵酱~ 优化 阅读 1,185
赞 17 收藏
二维码
手机扫码查看
反馈

先看效果,再看代码

我最近在优化一个图片密集型的页面,首屏加载慢得像老牛拉车。F12 一看,好家伙,一张 banner 图居然加载了 3MB 的高清图,而实际显示区域只有 600px 宽。这种浪费带宽的行为,必须干掉。

掌握srcset与sizes实现响应式图片加载的最佳实践

后来我直接上了 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> 标签才支持 srcsetsizes

另外,对于头像、图标这类小图,其实没必要搞多套尺寸——文件体积本身不大,折腾半天收益有限。我一般只对 >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),也有现成的插件能自动处理。总之,别手动生成,那是在浪费生命。

不过有个小问题一直没完美解决:当图片比例不固定时(比如用户上传的图),自动生成的尺寸可能裁剪不合理。我的临时方案是强制统一比例,虽然损失了部分灵活性,但至少保证了显示效果。如果你有更好的办法,欢迎评论区交流。

最后说两句

srcsetsizes 看起来简单,但细节很多。我折腾了两三天才调出满意的效果,中间还被产品经理问“为什么手机上图变模糊了”(其实是 sizes 写错了,加载了过小的图)。

但一旦配对,收益巨大——不仅省流量,还能提升 LCP(最大内容绘制)指标,对 SEO 也有帮助。Google PageSpeed Insights 经常因为这个给你加分。

以上是我踩坑后的总结,希望对你有帮助。这个技术的拓展用法还有很多(比如结合懒加载、CDN 优化),后续会继续分享这类博客。有更优的实现方式欢迎评论区交流。

本文章不代表JZTHEME立场,仅为作者个人观点 / 研究心得 / 经验分享,旨在交流探讨,供读者参考。
发表评论

暂无评论