link rel="preload" 失败时无事件通知,因它是声明式静默处理;必须用 new Image() 显式校验并监听 onload/onerror 才能可靠 fallback。

预加载图片失败不会触发 onerror,必须用 new Image() 显式校验并手动 fallback。
为什么 link rel="preload" 失败时什么都收不到
link rel="preload" 是声明式预加载,浏览器静默处理失败——既不抛错、也不触发事件,控制台可能只显示 404,但 JS 完全感知不到。它和 img 标签的 onerror 不是同一套机制。
- preload 的资源失败后,后续用到该 URL 的
img.src或background-image仍会重新发起请求,不会复用“失败缓存” - 无法监听
load/error,因为link元素本身不暴露这些事件 - 真正需要的是“预加载 + 可靠验证”,而不是依赖 preload 自身的容错能力
正确做法:用 new Image() 模拟预加载并监听结果
手动创建 Image 实例,才能拿到 onload 和 onerror,这是目前唯一稳定可控的方式。
- 必须在图片实际使用前完成校验,比如组件挂载时、页面初始化阶段
- 推荐写法:
const img = new Image();<br>img.onload = () => { console.log('预加载成功'); };<br>img.onerror = () => {<br> // 启用降级:base64 占位图、备用路径、或纯色背景<br> document.documentElement.style.setProperty('--hero-bg', 'url(data:image/png;base64,iVBOR...)');<br>};<br>img.src = '/assets/hero.webp'; - 避免重复创建:可用
Map缓存已校验过的 URL 状态,防止多次请求同一张图
常见坑:路径、协议、格式三重陷阱
很多“预加载失败”根本不是代码问题,而是环境限制被忽略。
立即学习“前端免费学习笔记(深入)”;
-
file://协议下所有 preload 和new Image()都大概率失败,必须用本地服务器(如 VS Code Live Server)测试 - WebP/AVIF 在 Safari 15 前、Android WebView 旧版中不支持,
new Image().src = 'x.webp'会直接进onerror,需提前 fallback 到 JPEG - 相对路径写成
img/src="logo.png"而非img/src="./logo.png",在构建后路径重写场景下极易 404 - CORS 图片若带
crossorigin属性,而服务端没返回Access-Control-Allow-Origin,onerror会触发,但换源也无效——得换托管方式
要不要预加载?先问三个问题
预加载不是银弹,滥用反而拖慢首屏。
- 这张图是否在 LCP(最大内容绘制)元素中?如果不是,
preload可能抢占关键资源带宽 - 是否已用
loading="eager"或显式设置width/height防抖动?这些比预加载更基础 - fallback 是否已内联(如 base64 SVG)?如果备用图还要网络请求,预加载就失去意义
最易被忽略的一点:预加载的图片 URL 必须和最终使用的 URL 完全一致(包括查询参数、大小写、斜杠结尾),否则浏览器不会复用,等于白做。



















