监听图片加载需先绑定load/error事件再设src,或检查img.complete;推荐用Promise封装,并在load后调用decode()确保解码完成,再结合IntersectionObserver实现可靠懒加载。

图片加载成功或失败如何监听
HTML5 中没有专门的「加载状态 API」,但 元素原生支持 load 和 error 事件,这是判断加载完成最直接、最可靠的方式。注意:这两个事件只触发一次,且仅在浏览器开始解析完图片数据(含解码)后才触发。
常见错误是监听 onload 但没处理缓存场景——已缓存的图片可能在 addEventListener 绑定前就完成了加载,导致事件丢失。必须在设置 src 之前绑定事件,或检查 complete 属性:
-
img.complete === true表示图片已加载完毕(含缓存、空 src、data URL 等情况) - 若
complete为true,可立即执行成功逻辑,无需等事件 - 若
complete为false,再监听load/error
用 Promise 封装图片加载更可控
手动管理事件容易出错,尤其在批量加载或配合 async/await 时。封装成 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 image: ${src}`));
img.src = src;
});
}
// 使用
loadImage('/logo.png')
.then(img => console.log('loaded', img.naturalWidth))
.catch(err => console.error(err));
注意点:
立即学习“前端免费学习笔记(深入)”;
- 不要用
document.createElement('img')后 append 到 body —— 不必要,还可能触发重排 -
new Image()创建的是无 DOM 的图像对象,轻量且安全 - 如果需兼容 IE9+,该写法完全可用;IE8 及以下不支持 Promise,需 polyfill 或回调替代
decode() 方法才是真正的「解码完成」信号
很多开发者以为 load 事件代表图片可渲染,其实不然。load 只表示资源下载并解析完成,但大图(尤其 WebP/HEIC)可能尚未解码,首次绘制仍会卡顿。HTML5 新增的 decode() 方法可显式等待解码结束:
img.addEventListener('load', async () => {
try {
await img.decode(); // 返回 Promise,解码完成才 resolve
console.log('Ready to render without jank');
} catch (err) {
console.error('Decode failed', err);
}
});
关键细节:
-
decode()在 Safari 14+、Chrome 54+、Firefox 69+ 支持,Edge 基于 Chromium 后也支持 - 调用前必须确保
img.complete === true,否则抛错 - 对小图影响不大,但对 >2MB 的高清图或懒加载场景,能避免首帧闪烁或 layout shift
懒加载图片怎么判断「真正可见并加载完」
单纯用 IntersectionObserver 监听进入视口,不等于图片已加载完成。典型坑是:元素刚进入视口就设置 src,但用户快速滚动,load 还没触发就移出视口,导致状态不可知。
稳妥做法是组合使用:
- 用
IntersectionObserver触发加载(设src) - 用
Promise.all(图片加载 Promise 数组)聚合所有待加载项 - 加载完成后才更新 UI 或取消 loading 状态
- 对已
complete的图片跳过重复加载(比如用户来回滚动)
最容易被忽略的一点:img.naturalWidth === 0 在加载失败或跨域未配 CORS 时也可能为真,不能单靠它判断是否成功 —— 必须以 load / error 事件或 decode() 结果为准。










