可靠图片降级需「多层 fallback 路径 + 状态隔离 + 渲染前校验」三者缺一不可:onerror 仅响应网络请求失败,对空 src、CORS 拦截、200 占位图等静默;loading="lazy" 不支持时不会读取 data-src,src 必须指向可用资源;picture/webp 降级须显式声明标准 MIME 类型并设强制兜底 img;video poster 失败需 new Image() 预校验。

直接写 loading="lazy" 或只靠 onerror 无法构成可靠降级,真正起作用的是「多层 fallback 路径 + 状态隔离 + 渲染前校验」三者缺一不可。
图片加载失败时 onerror 为什么总不触发
onerror 不是万能兜底,它只响应“请求发出后返回非 2xx 状态”或网络中断。以下情况它完全静默:
-
src=""、src=" "、src="broken.jpg"(路径解析失败未发请求) - 跨域图片没加
crossorigin,被 CORS 拦截(控制台报Blocked by CORS policy,但onerror不触发) - 服务器对 404 返回一个占位图(如
/img/missing.png?text=404),HTTP 状态码是 200,浏览器认为“加载成功”
实操建议:
- 必须用内联写法:
onerror="this.onerror=null;this.src='/assets/placeholder.svg'" - 占位图优先用
data:image/svg+xml;base64,...内联 SVG,彻底规避路径风险 - 动态创建的
img元素,onerror必须在appendChild()前绑定,否则首次错误捕获不到
loading="lazy" 降级失效的根本原因
浏览器不支持 loading="lazy" 时,它不会报错,也不会自动读取 data-src——它只是当这个属性不存在,然后按默认逻辑加载 src。如果此时 src 是空值、占位图或非法路径,结果就是白屏。
立即学习“前端免费学习笔记(深入)”;
可靠做法是让 DOM 始终处于可运行状态:
-
src必须指向一个可用资源:1px GIF、LQIP 图、或 SSR 渲染出的真实小图;绝不能留空或写src="#" -
data-src存高清图地址,且必须在 HTML 中静态存在(不能靠 JS 注入),否则断网时 JS 加载失败,data-src就丢了 - 关键图片(如商品主图、头图)跳过懒加载:直接写死
src,不加loading="lazy",也不设data-src
picture + webp 降级必须显式声明 MIME 类型
直接写 <img src="a.webp"> 在 Safari ≤13.1、IE、旧 Android WebView 中会静默留白——不是加载失败,而是浏览器根本不识别 image/webp,连请求都不发。
正确结构必须包含强制兜底:
<picture> <source type="image/avif" srcset="a.avif"> <source type="image/webp" srcset="a.webp"> <img src="a.jpg" alt="描述文字"> </picture>
注意要点:
-
<source type="...">的值必须严格匹配标准 MIME 类型:image/webp(不是webp或image/.webp) -
<img src="...">是强制兜底,不能省略,且路径必须可直访(避免带查询参数如a.jpg?format=webp) - 服务器返回的 Content-Type 必须与
type值一致,否则 Safari 会跳过该<source>
video poster 加载失败只能靠 new Image() 预校验
poster 属性本身不触发 error 事件,浏览器加载失败时只会留白或灰块,没有任何回调机会。
必须前置验证:
- 用
new Image()创建实例,设置onload和onerror回调 -
onload成功后再赋值video.poster = url;onerror则立即启用降级(如data:image/png;base64,...或纯色背景) - SSR 场景下,可在服务端提前校验路径有效性,或直接内联极简 data URL,避免依赖客户端 JS
最容易被忽略的一点:不要给 display: none 的 <video> 提前设 poster,旧版 WebView 仍可能发起请求,且失败后无任何补救途径。



















