图片懒加载
在一个内容丰富的电商页面或图片流社区中,如果用户一打开页面,服务器就把所有图片——包括滚动十几屏才能看到的——全部下载回来,会带来灾难性的后果:
- 首屏加载极慢:几十上百张图片的请求在开始阶段全部并发触发,带宽瞬间饱和。
- 流量浪费:用户只看了前三屏就关掉了,第十屏的图根本没机会被看到,却已经白白下载了。
- Core Web Vitals 暴跌:LCP(最大内容绘制)和 FID(首次输入延迟)双双恶化。
图片懒加载(Lazy Loading) 的核心思路是:只加载用户当前能看到(或即将看到)的图片,剩余图片暂时以占位符替代,等待用户真正滚动到附近时再加载真实资源。
方案一:原生懒加载(一行搞定)
现代浏览器已经原生支持图片懒加载!只需要给 <img> 标签加上 loading="lazy" 属性即可:
浏览器会自动在图片即将进入视口时才发起请求。
优缺点:
- ✅ 零 JS 代码,无性能损耗,实现成本极低。
- ✅ 现代浏览器全面支持(Chrome 77+,Firefox 75+,Safari 15.4+)。
- ❌ 无法精细控制触发时机(提前量固定由浏览器决定)。
- ❌ IE 完全不支持。
方案二:IntersectionObserver(生产首选)
这是目前生产环境中最主流、最灵活的方案。
原理与无限滚动类似:用标准的 IntersectionObserver API 观察每一张图片,一旦该图片进入(或即将进入)视口,就把它真实的 src 地址从 data-src 属性上读出来并赋值,从而触发图片的真实加载。
可交互 Demo:
图片懒加载演示
方案三:过时的 scroll + getBoundingClientRect(了解即可)
在 IntersectionObserver 普及之前,开发者们使用:
缺点:getBoundingClientRect() 会强制触发回流;scroll 事件即便加了节流依然有性能损耗;代码量多。——被 IntersectionObserver 完全取代。
高频面试题剖析
Q:如何实现图片懒加载?有哪几种方案?各有什么优缺点?
回答思路:
- 说明问题根源:图片资源大、首屏并发请求多会导致加载慢、流量浪费和 Core Web Vitals 下降(LCP 提高),懒加载是最直接的优化手段。
- 方案一(原生
loading="lazy"):直接给<img>加属性,零 JS 成本,浏览器原生支持,适合大多数场景;缺点是无法控制加载提前量,且 IE 不支持。 - 方案二(IntersectionObserver,重点):目前推荐的主流方案。初始时将真实
src存在data-src,用IntersectionObserver监听图片进入视口,一旦触发就赋值src,并立刻unobserve。可通过rootMargin控制提前量,异步无回流,性能极优。 - 方案三(
scroll+getBoundingClientRect):旧时代方案,需手动节流,有强制回流风险,现已基本被淘汰,用于了解历史背景。 - 工程补充:在 Vue/React 中可以封装成
LazyImage组件或使用v-lazy等指令库;还可结合图片的srcset、sizes属性实现响应式分辨率按需加在加上懒加载的双重优化。

