原生loading="lazy"可用但必须配齐width/height、srcset+sizes,且首屏图禁用;chrome 76+、firefox 75+、edge 79+、safari 15.4+支持,仅适用于静态和,需带src属性并避免父容器含transform或overflow:hidden。

原生 loading="lazy" 能用就用,但必须配齐 width/height、srcset+sizes,且首屏图绝不能加——否则不是懒加载,是埋雷。
什么时候该直接用 loading="lazy"
Chrome 76+、Firefox 75+、Edge 79+、Safari 15.4+ 均原生支持,零 JS 成本,适合静态列表页的非首屏 <img> 和 <iframe></iframe>。
- 必须带
src属性,空src或只写data-src会导致 404 或直接忽略 lazy 行为 - 必须有明确尺寸:内联
width/height、CSSaspect-ratio(iOS 15.4+)、或固定宽高样式(如img { width: 100%; height: 300px; }) - 禁用于
<picture></picture>内部的<source></source>——只有最外层<img>认这个属性 - 父容器若含
transform+overflow: hidden,Chrome 可能误判滚动根节点,导致图片永远不触发
为什么 IntersectionObserver 不可替代
当你需要控制视频、背景图、组件容器、动态插入的图片,或要兼容 Safari 15.3 及更早版本时,IntersectionObserver 是唯一可靠路径。
- rootMargin 设为
"200px"提前触发,避免快速滚动时白屏;设为"0px"则等完全进入才加载,体验差 - 每次回调中必须调用
observer.unobserve(img),否则已加载图片持续被监听,内存泄漏风险高 - 动态插入的
<img>,插入后需显式执行observer.observe(img),不能依赖自动发现 - 不要在
entry.isIntersecting === true前就赋值img.src,否则可能重复加载或跳过判断
srcset + sizes 必须和 loading="lazy" 共存
只加 loading="lazy" 不解决响应式问题:懒加载触发时视口宽度已定,若没 sizes,浏览器会按 fallback src 加载,大概率拉取桌面大图。
- 正确写法:
<img src="fallback.jpg" srcset="a-480w.jpg 480w, a-768w.jpg 768w" sizes="(max-width: 480px) 100vw, 50vw" loading="lazy"> -
src必须保留——老浏览器(如 Safari 15.3)忽略loading,但至少能按src加载 fallback - CDN 参数化 URL(如
img.jpg?w=480)要和srcset中路径一致,否则 lazy 触发后可能加载错尺寸 - 移动端 iOS 15.4 以下版本直接忽略
loading="lazy",此时srcset+sizes就是省流量的最后防线
哪些资源根本没法用原生 lazy
loading="lazy" 是 <img> 和 <iframe></iframe> 的专属属性,对其他资源无效,强行加只是自我安慰。
- CSS
background-image:属性加在<div> 上毫无作用,必须改用 <code><img>定位模拟,或手动用IntersectionObserver控制style.backgroundImage - 字体、JS、JSON 数据:原生不支持,得靠
rel="preload"(当前页关键资源)或rel="prefetch"(下一页可能资源)配合逻辑判断 - 通过
innerHTML插入的图片:部分 Firefox 版本在插入时已在视口内,仍会走 lazy 流程,造成短暂白屏,需插入后立即检查并手动赋值src - 服务端渲染(SSR)场景:HTML 中写了
loading="lazy",但 hydrate 前若元素已在视口,React/Vue 可能跳过加载,需用getBoundingClientRect().top 主动补救
真正决定懒加载是否生效的,从来不是“有没有写 loading="lazy"”,而是“浏览器有没有在正确时机、用正确尺寸、加载真正需要的那张图”——三者缺一,就是假懒加载。











