原生loading="lazy"仅在HTML解析阶段生效,若图片被JS动态插入、包裹在<table>或display:none容器中,或SSR hydration前DOM未就绪,则属性被忽略而失效。

原生 loading="lazy" 就够用,但必须配对 src、避开 <table> 和 display: none 容器,否则直接失效。
为什么 loading="lazy" 在长页面里经常不工作
浏览器只在解析 HTML 阶段读取 loading 属性,一旦元素被 JS 移动、包裹进 <table>、或父级设了 display: none,该属性就会被忽略——尤其在 SSR + hydration 场景下,DOM 已存在但尚未渲染完成,loading 可能压根没触发。
- 旧版 Safari(
-
<picture>或<source>里写loading="lazy"无效,必须落在最内层<img>上 - 首屏关键图若也加了
lazy,部分 Chrome 版本会因预加载策略延迟 1–2s,导致短暂空白
srcset + sizes 是响应式的基础,不是可选项
光靠 max-width: 100% 只控制显示尺寸,浏览器照样下载 2000w 的大图。真正节省带宽靠的是让浏览器在解析 HTML 时就选对资源。
-
sizes必须匹配真实布局断点,比如栅格占 1/3 宽,且 CSS 断点是768px,那就写sizes="(max-width: 768px) 100vw, 33.33vw" -
srcset混用w和x描述符(如"a.jpg 480w, b.jpg 2x")会导致整个属性被浏览器忽略,退化为只加载src - 服务端返回 WebP 时,
<picture>是刚需:<source type="image/webp" srcset="a.webp"><img src="a.jpg" alt="">,顺序错或缺<img>降级就崩
IntersectionObserver 手动实现滚动加载的几个硬约束
当需要 fallback 占位、校验网络、或处理背景图/组件级懒加载时,IntersectionObserver 不可替代,但必须守住几条线。
立即学习“前端免费学习笔记(深入)”;
- 创建 observer 时务必设
rootMargin: "200px",否则快速滚动时图片刚进视口才开始请求,用户一眼看到白块 - 回调里必须立刻调用
observer.unobserve(target),否则重复触发,且不卸载会造成内存泄漏 - 加载失败判断不能只看
onerror,要等img.naturalWidth === 0再 fallback,因为某些 CDN 会返回 200 空响应 - SSR 场景下首次 hydrate 前,得手动检查
element.getBoundingClientRect().top ,否则已在视口内的图会被跳过
真正容易被忽略的,是 sizes 的语法限制:它不支持 calc(),媒体查询里多一个空格(如 (max-width: 768px ))在旧版 Edge 中整条失效;而 loading="lazy" 在 <iframe> 上虽可用,但若 iframe 内容本身含大量图片,外部 lazy 对它内部无影响。



















