动态创建图标时须在插入DOM前绑定onerror;fallback图须用内联base64 SVG或绝对路径;全局捕获监听更可靠;框架中需避免响应式循环渲染;lazy加载下fallback图必须小而稳。

动态创建的
必须在插入DOM前绑定onerror
用document.createElement('img')批量生成图标时,如果先appendChild()再绑定onerror,90% 的失败会直接漏掉——错误在元素挂载瞬间就已触发,JS 还没来得及监听。浏览器不会等你。
正确做法是赋值后、挂载前就完成事件绑定:
const img = document.createElement('img');img.src = item.iconUrl;img.onerror = () => { img.src = '/assets/placeholder.svg'; img.onerror = null; };container.appendChild(img);
循环中防 fallback 图二次失败的关键写法
所有图标共用同一段降级逻辑,但 fallback 图路径一旦 404,整个循环就会静默白屏——onerror只触发一次,失败后不再回调,用户看不到任何提示。
必须确保 fallback 图本身零风险:
立即学习“前端免费学习笔记(深入)”;
- 优先用
data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMzIiIGhlaWdodD0iMzIiLz4这类内联 base64 SVG,不发请求、不解析路径、无跨域问题 - 若用外部路径,必须是绝对路径(如
/assets/icon-fallback.svg),禁用./或../ - 避免写成
onerror="fallback(this)"—— 函数未定义或作用域丢失时,整个 handler 解析失败,onerror彻底静默
为什么全局 error 监听比内联 onerror 更适合循环场景
循环生成的图标数量大、来源杂(可能来自 API、模板字符串、第三方组件),逐个写内联 onerror 不现实,且容易漏掉异步插入的项。
用捕获阶段全局监听能覆盖全部动态 <img>:
document.addEventListener('error', e => {if (e.target.tagName === 'IMG' && !e.target.dataset.fallbacked) {e.target.src = 'data:image/svg+xml;base64,...';e.target.dataset.fallbacked = 'true';}, true); // 第三个参数 true 表示捕获阶段
注意:true 不可省略,否则监听不到动态插入的 <img>;dataset.fallbacked 必须校验,否则同一张图被多次替换可能引发渲染抖动。
框架中循环渲染(Vue/React)要绕开响应式陷阱
在 v-for 或 map() 中直接写 @error="src = fallback" 或 onError={() => setSrc(fallback)} 极易导致无限 re-render:换图 → 触发状态更新 → 重新执行循环 → 再次创建 <img> → 又触发 onError。
安全方案是脱离响应式链,直操作 DOM:
- Vue:用
@error.once+ref,在回调里改imgEl.src,不碰 data 或 props - React:在
onError中加守卫if (e.target.src !== fallbackUrl),并提前用new Image().src = fallbackUrl预加载 fallback 图 - 两者都需确保 fallback 图 URL 在组件挂载前就已确定,不能依赖异步获取结果
最常被忽略的一点:循环生成的图标如果带 loading="lazy",首次滚动进入视口时才开始加载,这时 onerror 绑定早已完成,但 fallback 图若体积大或路径慢,仍会出现短暂空白——所以 fallback 图必须小而稳,别指望“稍后加载”。



















