onerror 无法自动限制重试次数,必须用计数机制(如 data-retry-count 或 Map)配合 this.onerror = null 防止死循环;但服务端返回 200 空响应时 onerror 不触发,需额外校验 naturalWidth。

onerror 本身不支持“最多执行三次”这种内置限制,必须手动计数并终止——否则 fallback 图片再失败,就会无限循环、卡死标签页。
为什么不能只靠 this.onerror = null
它只防第二次触发,但如果你的降级图(比如 fallback.png)也 404,浏览器仍会加载它 → 触发 onerror → 执行你写的代码 → 如果没做计数或清空逻辑,就又设一次 src,形成死链。关键不是“关掉事件”,而是“让后续加载不再走同一段逻辑”。
-
this.onerror = null是必须写的,但它只是第一道防线,不是兜底方案 - 真正要拦住第三次,得靠状态标记 + 条件判断,比如
data-retry-count或闭包变量 - 动态插入的
必须在appendChild()前绑定error监听器,否则首次失败根本捕获不到
用 data-retry-count 控制重试次数
给每个 加上初始计数,每次触发 onerror 先检查、再递增、再决定是否换图:
@@##@@<rect width="\'40\'" height="\'40\'" fill="\'#eee\'/"><text x="\'50%\'" y="\'50%\'" dominant-baseline="\'middle\'" text-anchor="\'middle\'" font-size="\'12\'" fill="\'#999\'">?</text>'; this.onerror = null; }"></rect>
- 前三次尝试:分别换
avatar.png→avatar.jpg→placeholder.svg(可扩展) - 第四次直接内联
data:image/svg+xml,彻底规避路径风险 - 最后一定写
this.onerror = null,防止 SVG 本身解析失败再进一次(极少见但存在)
用全局对象按 URL 统计重试次数(适合动态图)
当图片 URL 来自 JS 拼接、或大量同构资源时,靠 dataset 管理容易漏,更适合用内存对象记录:
const retryMap = new Map(); // key: url.pathname, value: count
function handleImgError(img) {
const url = new URL(img.src, location.href);
const key = url.pathname;
const count = (retryMap.get(key) || 0) + 1;
if (count > 3) {
img.src = 'data:image/svg+xml,...'; // 内联兜底
img.onerror = null;
return;
}
retryMap.set(key, count);
img.src = img.dataset.fallback || '/static/placeholder.png';
}
- 按
pathname计数,避免相同图片不同查询参数(如?v=1)被当成多个资源 - 不依赖 DOM 属性,适合
document.createElement('img')场景 - 注意:页面长期不刷新时,
retryMap会持续增长,关键业务可加 TTL 清理逻辑
最容易被忽略的点:服务端返回 200 却没图,onerror 根本不触发
Cloudflare 错误页、Nginx 404 重定向到 HTML、CDN 返回空响应体——这些都算“加载成功”,onerror 静默失效。此时无论你设几次重试都没用。
- 验证方式:打开 DevTools → Network → 找该请求 → 看 status 是不是 200,再点 preview 看有没有图像内容
- 真正鲁棒的做法是结合
格式降级 +onload后校验naturalWidth === 0,但这已超出onerror能力范围 - 所以“最多三次”只解决网络层失败,不解决语义层失败;后者得靠额外 JS 校验
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











