loading="lazy"必须直接写在<img>标签上,且需同时满足:src为真实URL、有可推断尺寸、父容器无干扰样式、图片为初始HTML静态结构,否则退化为eager。

loading="lazy"写在哪、怎么写才真正生效
直接写在 <img> 标签上,且必须同时满足四个硬性条件,否则浏览器会无视它、退化为 loading="eager"。它对 <source>、<picture>、background-image 无效,也不支持 JS 动态插入的节点。
-
src必须是真实 URL(不能是src="#"或空值),也不能是 data URL - 必须有可推断的尺寸:显式写
width和height属性,或 CSS 中设固定宽高(aspect-ratio在 iOS Safari 15.4 前不安全) - 父容器不能含
transform、overflow: hidden、will-change、contain: layout等干扰滚动根检测的样式 - 图片必须属于初始 HTML(SSR 渲染出的静态结构),不能由 Vue/React 的
v-for或map()动态生成
首屏图为什么加了 lazy 还是白屏或闪烁
因为浏览器默认把没声明 loading 的图当作 eager,但 Safari(尤其 iOS 16.3 及更早)会把未声明的图也懒掉——你没写 loading="eager",它就真不加载了。首屏关键图(Banner、Logo、头像)必须显式声明,且建议配合 <link rel="preload" as="image">。
-
preload的href必须和<img>的src完全一致,否则算两个资源,重复下载 - 禁止给
loading="lazy"的图加preload,预加载和懒加载逻辑冲突 - 用
position: absolute; top: -9999px占位的图,浏览器认为“已渲染但不可见”,不触发懒加载,反而可能漏加载
srcset + sizes 不配齐,lazy 就白配
只写 srcset 不写 sizes,浏览器无法知道这张图在页面中实际占多宽,只能瞎猜。移动端常拉取 2000w 桌面图,延迟加载反而放大带宽浪费。
-
sizes值必须反映**实际占位宽度**,比如sizes="(max-width: 480px) 100vw, (max-width: 768px) 50vw, 33vw" -
src必须保留,作为所有浏览器的 fallback(Safari 15.4 之前忽略loading,但至少还能按src加载) - CDN 图片 URL 含参数(如
photo.jpg?w=480)时,确保srcset中路径与之完全一致,否则 lazy 触发后加载错尺寸
什么时候该放弃 loading="lazy",改用 IntersectionObserver
当你需要提前加载(比如滚动前 300px 就触发)、加载失败 fallback、LQIP 占位、自定义阈值或兼容 iOS 14 / 微信 X5 内核时,原生 lazy 就不够用了。但注意:别混用,否则图片可能加载两次。
立即学习“前端免费学习笔记(深入)”;
- 先检测支持性:
'loading' in HTMLImageElement.prototype,只对不支持的环境挂载 observer - observer 回调里只做两件事:
img.src = img.dataset.src+observer.unobserve(img) - 监听前过滤已在视口内的元素:
Array.from(images).filter(img => img.getBoundingClientRect().top < window.innerHeight),避免刚进页就批量触发 - 别在回调里改 class、触发动画、调 AJAX 或操作其他 DOM——这些都会打断渲染帧
loading="lazy" 是零成本基线,但它不处理错误、不支持占位、不暴露加载时机。真正容易被忽略的是:它依赖浏览器首次 layout 后批量判断,而 layout 受 CSS 阻塞、JS 执行、父容器样式等多重影响——你以为加了就懒了,其实它可能根本没进入懒加载队列。



















