onerror是唯一可靠入口,必须配this.onerror=null防递归;推荐用base64内联占位图确保fallback可用,且需注意服务端返回200但非图片内容时onload仍触发的“假成功”问题。

图片加载失败时如何触发降级逻辑
浏览器原生不提供“图片加载完成与否”的事件钩子,但 onerror 是唯一可靠入口。它只在资源请求返回非 2xx 状态、解析失败(如损坏的 JPEG)、或跨域未配 CORS 且被拒绝时触发——注意:404、500、网络中断、src 为空或非法 URL 都会进这里。
常见误用是把 onerror 放在 <img> 标签里却没重置错误状态,导致降级图再失败时无限递归。正确做法是首次触发后立刻移除监听或清空 onerror:
@@##@@
- 必须写
this.onerror=null,否则 fallback 图加载失败会再次调用自身,栈溢出 -
src替换为相对路径时,确保 fallback 图本身可访问(建议用绝对路径或 base64 内联小图) - 不要在
onerror里调用外部函数却不处理函数不存在的情况,否则报错会吞掉原本的加载失败信号
用 JavaScript 监听并区分加载成功与失败
想做更精细控制(比如打点、切换 loading 状态、尝试多个 fallback),就得用 JS 绑定事件。关键点在于:load 和 error 是互斥的一次性事件,但 error 不保证一定触发(例如某些 CORS 场景下静默失败)。
推荐用 Promise 封装,统一处理超时和错误:
function loadImage(src) {
return new Promise((resolve, reject) => {
const img = new Image();
img.onload = () => resolve(img);
img.onerror = () => reject(new Error(`Failed to load ${src}`));
img.src = src;
});
}
<p>// 使用
loadImage('avatar.webp')
.catch(() => loadImage('avatar.jpg'))
.catch(() => { /<em> 最终降级 </em>/ });</p>
-
new Image()创建的是独立实例,不影响 DOM 中的<img src="photo.jpg" onerror="this.onerror=null; this.src='fallback.png'">元素,记得手动插入或替换 - 不加超时控制的话,遇到 DNS 卡死或服务器无响应,
Promise会永远 pending;生产环境建议加setTimeout包裹 - WebP 兼容性检测不能只靠文件后缀,得结合
document.createElement('canvas').toDataURL('image/webp')判断
CSS 背景图无法用 onerror,得换思路
background-image 没有 onerror,浏览器也不会抛错,加载失败时就留白。要实现降级,只能提前预判或用 JS 注入。
将调研报告、方案对比、数据分析、需求梳理、会议纪要、设计说明、代码评审、学习笔记等任务产物输出为可在浏览器直接打开的单文件 HTML,替代 Markdown 作为最终交付物。Markdown 在信息密度、视觉清晰度和可分享性上已跟不上 AI 产物的丰富度,HTML 能同时承载排版、表格、SVG 等。
最稳妥的方式是服务端输出带多重 fallback 的 CSS 值:
.avatar {
background-image: url('photo.avif');
background-image: url('photo.webp');
background-image: url('photo.jpg');
}
浏览器按顺序解析,支持的格式优先使用,不支持就往下落。但注意:url() 里的路径仍可能 404,此时整条声明失效,不会自动跳到下一条。
- 必须确保每个
url()都指向真实存在的资源,哪怕只是 1×1 透明 GIF - 不能依赖
@supports (background-image: url(foo.avif)),因为该语法不被支持;可用 JS 检测后动态加 class - 如果用
loading="lazy"的<img>,其onerror在懒加载触发前不会执行,需监听IntersectionObserver后再设置src
服务端返回 200 但内容不是图片怎么办
这是最容易被忽略的坑:Nginx 配错了 MIME 类型、CDN 缓存了 HTML 错误页(比如 503 页面被缓存成 200)、PHP 脚本输出了 warning 文本——浏览器收到 200 + text/html,onload 仍会触发,但图片显示为破损图标。
验证方式很简单:打开 DevTools → Network → 找对应图片请求 → 看 Response Headers 的 Content-Type 是否为 image/*;再右键“Open in New Tab”,看是否真能渲染。
- 前端无法直接读取响应体或 MIME 类型,所以这种“假成功”只能靠服务端修复
- 若必须前端兜底,可在
onload后用img.naturalWidth === 0做二次判断(加载失败时该值为 0),但有兼容性风险(Safari 旧版不支持) - CDN 或反向代理务必配置好
location ~* \.(jpg|jpeg|png|webp|avif)$ { add_header Content-Type image/$1; }
实际部署时,降级链路越长,首屏时间越不可控。真正关键的图(如头像、商品主图)建议只设一级 fallback,并确保 fallback 图体积足够小、CDN 命中率高。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!










