必须用 srcset + sizes + 服务端 faststart + loading="eager" + onerror 内联降级,因浏览器在 HTML 解析时即发起图片请求,JS 改 src 会触发二次加载,iOS Safari 弱网下常静默中断且不触发 onerror。

直接用 srcset + sizes + 服务端 faststart 处理,再配合 loading="eager" 和 onerror 降级逻辑,才能让高清图在弱网下真正“可退”——只靠前端 JS 监听网速或改 src 基本无效。
为什么改 src 或用 JS 切图在弱网下不靠谱
浏览器对 <img> 的加载决策发生在 HTML 解析阶段,一旦解析到带 src 的 <img>,就立刻发起请求;后续 JS 修改 src 只会触发第二次请求,首屏已卡在高清图上。更糟的是:iOS Safari 在弱网下常提前终止请求但不触发 onerror,导致降级逻辑完全失灵。
- 不要依赖
navigator.onLine:它只反映系统网络开关状态,和真实 RTT 无关 - 避免在
DOMContentLoaded后才切图:此时高清图可能已下载 80%,浪费已发生的流量 - 别用
fetch()测速再决定加载哪张:多一次 round-trip,首屏延迟翻倍
srcset + sizes 是唯一可控的原生降级入口
浏览器根据 sizes 计算出图片“该显示多宽”,再结合 srcset 中的 w 描述符(如 400w)选最接近的一张,这个过程在解析 HTML 时就完成,不依赖 JS,也不等 CSSOM。
- 必须写
sizes:否则浏览器按视口宽度估算,移动端常误选 2x 图 - 示例写法:
<img src="photo-800.jpg" srcset="photo-400.jpg 400w, photo-800.jpg 800w, photo-1600.jpg 1600w" sizes="(max-width: 768px) 100vw, 50vw"> - 关键点:把最小尺寸(如
400w)放在srcset最前面——部分旧版 Android 浏览器会取第一个可用项
首屏图必须加 loading="eager",且禁用懒加载
移动端 loading="lazy" 在 iOS 15.4 以下版本直接被忽略,而现代 Chrome/Edge 对首屏图默认 eager 加载;但若你用了框架或 CMS 自动加了 loading="lazy",就会强制延迟,导致弱网下首屏空白。
立即学习“前端免费学习笔记(深入)”;
- 检查渲染后 DOM:确认首屏
<img>没有loading="lazy" - 显式写
loading="eager",比什么都不写更可靠 - 搭配
decoding="async"防止图片解码阻塞主线程
onerror 降级要防“静默失败”,且必须内联
弱网下图片请求常超时中断,但 onerror 不一定触发(尤其 iOS);即使触发,若降级图路径是相对地址,JS 禁用或路径错位会导致 404。
- 降级图用绝对路径或 base64 内联小图:
<img src="high.jpg" onerror="this.src='data:image/svg+xml;base64,...'"> - 避免在外部 JS 里绑定:
img.addEventListener('error', ...)—— 此时图片可能已报错完毕 - 服务端需确保所有尺寸图都存在:若
photo-400.jpg404,浏览器不会自动 fallback 到photo-800.jpg
真正起作用的降级,不是“等网变差再切”,而是从 HTML 解析第一帧就开始选最轻量的可行资源——结构、属性、服务端处理,三者缺一不可。最容易被忽略的是 sizes 值没配对容器实际宽度,结果写了 srcset 也白搭。



















