原生 loading="lazy" 通过延迟非视口图片请求来减少初始网络竞争,从而防阻塞关键渲染路径;但缺失宽高、transform+overflow:hidden、fixed/sticky 容器中易失效,首屏图禁用,旧浏览器或需提前加载时须用 IntersectionObserver 替代。

原生 loading="lazy" 不会阻塞 HTML 解析,但错误使用会让浏览器降级为 eager 加载,实际失去防阻塞效果。
为什么 loading="lazy" 能防阻塞关键渲染路径
浏览器在解析 HTML 时,遇到 <img> 标签默认会立即发起图片请求 —— 即使该图远在页面底部。这个请求本身不阻塞 DOM 构建,但会抢占网络带宽、触发 DNS 查询、建立 TCP 连接,间接拖慢关键资源(如 CSS、首屏 JS)的获取。而 loading="lazy" 告诉浏览器:“别急着发请求,等它快进视口再说”。这一步跳过,就从源头减少了初始网络竞争。
注意:它不改变 DOM 构建顺序,也不影响 CSSOM 或 JS 执行,只干预资源调度时机。
-
loading="lazy"是声明式行为,由浏览器底层调度器控制,无需 JS 参与,不触发重排重绘 - 未设置该属性的
<img>默认行为是loading="eager",等价于不加属性 - 它对
<iframe>同样有效,但对background-image无效(CSS 层面无法声明懒加载)
loading="lazy" 失效的三个典型场景
加了属性却没懒加载,几乎都出在这几个地方:
立即学习“前端免费学习笔记(深入)”;
- 图片缺失明确的
width和height(内联或 CSS 均可),导致浏览器无法预留空间,部分版本 Safari 直接降级为 eager -
<img>父容器用了transform+overflow: hidden,Chrome 会误判滚动根节点,使 Intersection Observer 失效,图片永远不触发加载 - 图片位于
position: fixed或position: sticky容器中,且该容器不是视口本身 —— 浏览器可能无法正确计算其可视性
验证是否生效:打开 DevTools → Network 面板,滚动页面,观察图片请求是否随滚动延迟发出;若首屏一加载就全量请求,说明已失效。
何时必须用 IntersectionObserver 替代原生 lazy
原生方案够用,但以下情况绕不开 JS 方案:
- 需支持 Safari < 15.4、Firefox < 75、旧版 Edge —— 这些浏览器完全不识别
loading属性 - 想提前加载(比如滚动到距离视口还有 200px 时就开始请求),
rootMargin: "200px"比依赖浏览器默认阈值更可控 - 图片通过
<picture>+<source>动态切换格式,且需统一控制各<source>的加载时机(原生loading只作用于外层<img>) - 需要配合
decoding="async"异步解码,避免大图解码阻塞主线程 —— 这个属性只能在 JS 赋值src后设置
关键细节:observer.unobserve(img) 必须执行,否则同一张图可能被多次触发;img.src = img.dataset.src 是最稳妥赋值方式,比操作 srcset 或 data-src 更少歧义。
首屏图片千万别懒加载的硬边界
“懒”是策略,不是教条。以下图片一旦加 loading="lazy",反而损害性能和 SEO:
- HTML 中物理位置在首屏内的
<img>(即未滚动就能看到),包括 banner 主图、logo、表单图标 - 被
position: absolute/fixed覆盖在首屏之上的图(即使 DOM 位置靠后) - 用于关键视觉反馈的图,比如加载状态中的占位图替换图 —— 用户等待的就是它出现
判断依据不是“有没有滚动”,而是“用户打开页面瞬间是否期待看到这张图”。漏掉这个,优化就变成负优化。



















