图片资源往往占据页面传输体积的大头,真正影响体验的也常常不是脚本本身,而是首屏关键图是否及时到达、非首屏图片是否过早请求。把图片懒加载和 WebP、AVIF 这两类手段配合起来看,思路会更清晰:前者解决“不该现在加载的别急着加载”,后者解决“必须加载的尽量更小”。
这篇文章按实际落地顺序来拆解:先看浏览器原生能力能解决多少,再看什么时候需要用 IntersectionObserver 精细控制,最后补上现代图片格式与首屏策略。读完后,你可以判断哪些图片该懒加载、哪些不能动,以及格式升级该怎么做才不影响兼容性。
原生懒加载:先用最低成本拿到收益
如果项目面向的是现代浏览器,图片懒加载最直接的做法就是使用 loading 属性。当前主流浏览器已经提供原生支持:Chrome 76+、Edge 79+、Firefox 75+、Safari 15.4+。

对非首屏图片,可以直接在 <img> 上添加 loading="lazy"。浏览器通常会在图片接近视口前大约 1250px 时发起请求,具体提前量会随设备和环境调整。这种方式的优点是改动很小,不需要额外脚本,也能快速减少首屏阶段的非必要请求。
但这里有一个判断标准不能错:首屏关键图不要懒加载。对于首页主视觉、商品主图、文章头图这类明显会参与首屏展示、甚至可能成为 LCP 候选的图片,要么不写这个属性,要么显式设置为 loading="eager",确保浏览器立即处理。
此外,原生懒加载不仅适用于 <img>,对 <iframe> 同样生效。不过如果图片是通过 CSS background-image 挂载的,仅靠这个属性并不能延迟加载,通常还需要结合 JavaScript,在合适时机再动态写入 background-image。
还有一个常被忽略的配套动作,是为图片提前声明尺寸。无论是直接写 width、height,还是通过 CSS 的 aspect-ratio 预留比例空间,目的都一样:避免图片加载完成后把原有布局顶开,造成 CLS。
需要更细控制时,用 IntersectionObserver
原生懒加载已经覆盖了大量场景,但一旦需求变复杂,IntersectionObserver 会更稳。典型情况包括:需要兼容旧浏览器、想把触发距离从默认策略改成“提前 200px 加载”、或者要接入占位图、错误回退、重试逻辑时。
一个常见实现方式是把真实图片地址先放进 data-src,而 src 使用一个极小的占位图,例如 1×1 透明 base64:
data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==这样页面初始渲染时不会立刻请求真实资源,等观察器确认元素即将进入视口,再把 data-src 替换到 src 上。
实现上要注意复用 Observer。更合理的写法是创建一个全局或模块级的单个 Observer 实例,用它去监听一组图片,而不是每个元素都新建一个 observer。后者虽然也能工作,但会带来额外的实例开销,页面图片一多就不划算。
如果你希望用户滚动到图片位置时几乎感受不到等待,可以通过 rootMargin: "200px" 之类的设置,让图片在进入可视区之前就开始加载。这个值并没有统一标准,列表流内容、瀑布流和长文详情页的最佳距离可能都不一样,通常需要结合网络条件和内容密度来调。
当图片成功加载后,记得执行 unobserve(el),避免后续重复触发同一逻辑。如果请求失败,也不要让页面直接显示破图,应该在 error 事件里回退到备用图,必要时再叠加重试策略。
格式升级:把必须加载的图片继续压小
懒加载解决的是请求时机,格式升级解决的是资源体积。对页面性能来说,这两件事应该同时做。

在现代格式里,AVIF 和 WebP 是最常用的两条路线。对于首屏大图、LCP 图像这类体积大、影响关键渲染路径的资源,AVIF 优先级更高。按照原文给出的经验,同等质量下它通常比 JPEG 小 50%+,同时支持更高压缩率和更好的色深表现。
如果场景更看重通用性、编码速度和兼容范围,WebP 往往更适合作为主力格式。它在浏览器侧支持更广,生成速度也更快,体积通常仍比 JPEG 或 PNG 小 25–35%。这使它非常适合大多数内容图、通用插图和图标资源。
实际部署时,最稳妥的方式不是只押一种格式,而是通过 <picture> 和 <source> 做渐进式降级,让浏览器按能力选择:
<picture>
<source srcset="hero.avif" type="image/avif" />
<source srcset="hero.webp" type="image/webp" />
<img src="hero.jpg" alt="..." />
</picture>这套链路的核心顺序就是 image/avif → image/webp → image/jpeg。支持 AVIF 的浏览器拿到最小资源,不支持的再自动降级到 WebP 或 JPEG,兼容性和性能可以一起兼顾。
如果项目后面还有服务端或 CDN,可以进一步利用内容协商。也就是根据请求头里的 Accept 返回对应格式,并为静态资源设置长期强缓存:
Cache-Control: public, max-age=31536000, immutable这样做能减少重复传输,尤其适合经过构建指纹处理、内容变更即文件名变化的图片资源。
首屏图、响应式图片和 SPA 场景的细节处理
图片优化容易做出“看起来做了很多,实际收益打折”的结果,问题通常就出在这些细节上。
首先,首屏关键图除了不要懒加载,还应补上更明确的优先级提示。可以加上 fetchpriority="high" 和 decoding="async",前者告诉浏览器这是高优先级资源,后者帮助图片解码更高效地融入渲染流程。
其次,响应式图片不能只做一半。如果页面使用了不同尺寸图片源,就要同步配置 srcset 和 sizes。否则即便懒加载生效,浏览器仍可能因为尺寸判断不准确而先拉取低清版本,影响最终显示质量。
对单页应用来说,图片优化还要考虑“页面不是一次性生成”的问题。比如路由切换、新列表追加、组件异步挂载之后,新的图片元素并不会自动进入既有监听流程,这时需要在组件挂载后重新执行 observer.observe(),确保新插入节点也能按预期懒加载。
最后,有些图片从语义上就不该进入懒加载名单。轮播首帧、商品主图、Banner 主视觉这类资源,往往天然就是 LCP 候选。如果为了“全站统一策略”把它们也懒加载,反而会拖慢首屏关键指标,这类图应始终优先请求。
怎么判断一张图该不该懒加载
如果要把这套方法真正落地,可以用一个简单标准来判断:先区分“首屏关键图”和“滚动后才会看到的内容图”。前者关注尽快展示,优先考虑 loading="eager"、fetchpriority="high"、AVIF/WebP 和尺寸声明;后者关注减少初始请求,适合使用 loading="lazy" 或 IntersectionObserver 延迟加载。
再往下一层看,懒加载和格式升级不是替代关系,而是叠加关系。非首屏图片可以懒加载,也应该尽量使用 WebP 或 AVIF;首屏关键图虽然不能懒加载,但同样值得升级格式、配合缓存和响应式资源策略。只有把“时机”和“体积”同时处理好,LCP、CLS 和首屏加载时间才会稳定改善。













