loading="lazy" 失效主因是浏览器降级为eager,仅对初始HTML中存在、不在首屏、有明确宽高、父容器无transform/overflow干扰的img生效;常见失效包括缺宽高、JS动态插入、写在source上、置于table内。

loading="lazy" 为什么写了却没效果
加了 loading="lazy" 但图片还是全量加载,大概率不是写错了,而是浏览器直接降级为 loading="eager"。它只对「初始 HTML 就存在、不在首屏、有明确宽高、父容器没干扰样式」的 <img> 生效。
常见失效场景:
-
<img>缺少width和height(或未用 CSSaspect-ratio推断尺寸),Safari 可能直接跳过懒加载 - 父容器用了
transform、overflow: hidden或will-change: transform,破坏滚动根检测 - 图片是 JS 动态插入的(React/Vue 渲染、
innerHTML注入),不属于初始 DOM,原生懒加载不识别 - 错把
loading="lazy"写在<source>上——该属性只对最内层<img>有效 - 图片放在
<table>里(<td>中),Chrome 基本不触发懒加载
真正能用的最小占位图 HTML 写法
如果必须用内联方式(CMS/模板限制),这个结构才稳定:
<img src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='1' height='1'/>"
loading="lazy"
width="320"
height="240"
alt="描述文字">
关键点:
立即学习“前端免费学习笔记(深入)”;
- 用 SVG Base64 占位(
src="data:image/svg+xml,...")——体积小、无请求、不触发错误 - 真实地址存
data-src,不是src,避免初始请求 - 必须带
width/height或 CSSaspect-ratio,否则布局抖动+懒加载失效 -
alt不可省,既是无障碍基础,也是 fallback 文案
onerror fallback 为什么经常不显示
onerror 失效不是 JS 问题,而是浏览器阻止了二次错误:第一次加载失败后,this.src='fallback.jpg' 触发第二次请求,若 fallback 路径也错,浏览器静默丢弃第二次 onerror。
稳定写法必须含 this.onerror=null:
<img src="broken.jpg"
onerror="this.onerror=null;this.src='/assets/placeholder.svg'"
alt="加载失败">
注意:
- fallback 路径必须是绝对路径(如
/assets/...)或明确可访问的相对路径(如../images/...) - 推荐用
.svg占位图——无渲染失败风险,天然透明,体积通常 - 不要依赖内联
onerror做核心 fallback;真正要兜底,得用 JS 统一监听 +IntersectionObserver补充
需要提前加载?别用 loading="lazy"
原生 loading="lazy" 没有偏移配置项,它只在图片进入视口时才开始预取(阈值固定,约 1250px 下方)。如果想“离视口还有 200px 就加载”,必须换方案。
用 IntersectionObserver 手动控制:
const observer = new IntersectionObserver(
entries => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const img = entry.target;
img.src = img.dataset.src;
img.removeAttribute('data-src');
observer.unobserve(img);
}
});
},
{ rootMargin: '200px' } // ← 提前 200px 加载
);
document.querySelectorAll('img[data-src]').forEach(img => observer.observe(img));
HTML 配合写:
<img
src="data:image/svg+xml,%3Csvg%3E"
width="320" height="240"
alt="描述">
要点:
-
rootMargin: '200px'表示上下各扩展 200px,图片离视口还有 200px 就触发 - 支持写成
'100px 0px 200px'(上右下左),适配固定 header 等场景 - 务必设宽高,否则
getBoundingClientRect()计算不准,observer 可能漏触发
真正容易被忽略的是:所有这些方案都依赖图片在初始 HTML 中存在。JS 动态插入的 <img>,无论怎么写 loading 或绑定 observer,都得手动调用 observer.observe(),否则根本不会进队列。



















