最简图片加载方式是直接用<img>标签加src,但缺乏容错性;应通过srcset+sizes实现响应式尺寸选择、<picture>实现格式与条件降级、loading="lazy"配合Intersection Observer延迟加载、服务端根据Save-Data头返回轻量图。

直接用 <img> 标签加 src 是最简方式,但一旦图片加载失败、网络差或用户禁用图片,页面就只剩一个空框或占位符,还照常消耗流量。降级不是“让图不显示”,而是“让图在必要时换一种更轻、更可控的方式呈现”。
用 srcset + sizes 控制不同设备加载不同尺寸图
浏览器会根据屏幕宽度、像素密度和 sizes 提供的提示,从 srcset 列表里选最匹配的资源。这能避免手机加载 4K 图、桌面加载 320px 小图。
常见错误是只写 srcset 不配 sizes,导致浏览器默认按 100vw 估算,选错尺寸。
-
srcset必须用逗号分隔,每项后跟宽度描述符(如320w)或像素密度描述符(如2x) -
sizes是媒体条件 + 宽度值的组合,例如(max-width: 768px) 100vw, 50vw - 如果没配
sizes,且srcset全是x描述符,浏览器只能靠设备像素比猜,容易多下图
示例:
立即学习“前端免费学习笔记(深入)”;
<img
src="photo-320.jpg"
srcset="photo-320.jpg 320w,
photo-768.jpg 768w,
photo-1200.jpg 1200w"
sizes="(max-width: 768px) 100vw, 50vw"
alt="风景照">
用 <picture> 实现格式降级与条件加载
当需要支持 WebP/AVIF 但又要兜底 JPEG,或想为低带宽用户返回小图时,<picture> 是唯一标准方案。它不改变语义,但把资源选择权交给开发者。
关键点在于:<source> 按顺序匹配,第一个满足条件的生效;<img> 是最终 fallback,必须存在且带 src 和 alt。
- 用
type属性声明 MIME 类型,如image/webp,浏览器不支持时跳过该<source> - 用
media做响应式切换,例如(max-width: 480px)加载压缩版 - 不要在
<source>里写src,只写srcset+sizes或media
示例(优先 WebP,小屏用更小 JPEG):
<picture> <source type="image/webp" srcset="photo.webp 1x, photo@2x.webp 2x"> <source media="(max-width: 480px)" srcset="photo-small.jpg"> <img src="photo.jpg" alt="风景照"> </picture>
用 loading="lazy" 和 Intersection Observer 配合延迟加载
首屏外图片默认不请求,能显著降低初始流量——但要注意:它只对垂直滚动有效,对横向滚动、iframe 内嵌、或 display: none 的容器无效。
真正要控制流量,得结合用户行为判断是否“真需要这张图”:
-
loading="lazy"是基础,但不能替代业务逻辑(比如后台标签页里的图不该加载) - 对关键路径图片(如商品主图、登录背景),禁用 lazy,避免白屏等待
- 用
IntersectionObserver手动监听时,记得设rootMargin提前触发,否则用户已滑到位置才开始加载,体验卡顿 - 加载失败时,应降级为纯色背景或 SVG 占位,而不是留空或显示 broken image
服务端配合:通过 User-Agent 或 Save-Data 头返回轻量图
前端再怎么优化,也绕不开“第一张图是谁发的”。如果后端能识别 Save-Data: on 请求头,或从 UA 判断是低端机型,就可以直接返回压缩率更高、尺寸更小的图。
这个动作无法被前端模拟,是真正从源头减少字节数的手段:
- Cloudflare Workers、Nginx Map、或 Node.js 中间件都可读取
Save-Data头 - 别只依赖客户端 JS 检测网络类型(
navigator.connection.effectiveType),它不可靠且有延迟 - 对随机壁纸类接口,可加参数如
?quality=60&w=800,由前端按需拼接,后端统一处理
复杂点在于:同一张图的多个变体要缓存分离,否则 WebP 用户可能拿到 JPEG 的缓存。CDN 缓存键必须包含 Accept 和 Save-Data 等字段。



















