懒加载(Lazy Loading)一直是前端性能优化的常见方式。不知道从什么时候开始 <img loading="lazy">
这样的属性方案开始大行其道,很多同学并不了解 <img loading="lazy">
的作用,就直接把它放到项目里进行使用,不知不觉就为自己埋下了大坑!
loading="lazy" 的作用

根据 MDN
的介绍,我们可以很清楚的了解 <img loading="lazy">
的作用, 很多同学在看到这个属性后都会“虎躯一震”,觉得:“这简直是完美解决懒加载的方案啊,不用写 JS 了,真香!”
但现实远没有那么理想:浏览器的懒加载行为远不如你想象中的那么“聪明”:
1. 加载触发点不稳定,不可控
浏览器内部定义了一个“可见区域附近的预加载区域”(preload margin),当图片即将进入视口时才加载。
但这个区域是 不可控的!不同浏览器、不同版本、甚至不同网络环境下的触发条件都不一样!
举个例子:
- 在 Chrome 中可能是距离视口下方 1250px 就开始加载。
- 在 Safari 中可能必须接近视口 200px 才触发。
所以你可能会发现用户已经快滚到图片了,但图片还没开始加载,造成“白屏”或者“图片加载卡顿” 的问题
2. 首屏关键图被延迟加载
很多同学直接在所有 <img>
标签上加上 loading="lazy"
,包括首屏 Banner、Logo、首图等关键视觉内容。
这是个大坑!
首屏图片本应尽早加载,提升 FCP(首次内容绘制)性能,但 loading="lazy"
会把它推迟,导致页面看起来很慢。
比如,如下代码就会导致 FCP 时间变长、LCP 分数下降、用户体验变差
<!-- 下面的 Logo 是首屏元素,却用了 lazy,导致 Logo 显示很慢 -->
<img src="/logo.png" loading="lazy" alt="品牌Logo" />
3. 加载状态不可监听
在实际开发中,我们通常需要 监听图片懒加载的状态,比如:增加占位图
各种色块的占位图
但是,loading="lazy"
不可监听!这就会导致以下功能完全无法实现:
- 没法给图片加载失败加容错处理(比如加载失败替换默认图)
- 没法实现渐进式加载体验(如先显示模糊图再加载高清图)
这些需求,loading="lazy"
全都做不到!
4. 只能用于 <img>
和 <iframe>
,场景非常局限
如果你想懒加载:
都不能用 loading="lazy"
,完全不适配,只能乖乖用 JS 自己监听。
正确的方案:IntersectionObserver
当浏览器原生的 loading="lazy"
不能满足我们在 加载控制、动画处理、骨架屏、兼容性、多元素懒加载 等方面的需求时,更强大、更灵活、更现代的方式就是: IntersectionObserver
。

IntersectionObserver
是浏览器提供的一个 API,它允许你观察某个 DOM 元素是否进入或离开视口(或某个指定容器),从而可以在适当时机触发事件,比如:
它会持续监听一个目标元素是否“交叉”进入指定容器(通常是视口),并触发回调:
const observer = new IntersectionObserver(callback, options)
observer.observe(targetElement)
以 Vue
为例,我们可以通过如下方式来直接完成 懒加载指令
// vue3 指令实现(使用 @vueuse/core 的 useIntersectionObserver)
app.directive('lazy', {
mounted(el, binding) {
const { stop } = useIntersectionObserver(
el,
([{ isIntersecting }]) => {
if (isIntersecting) {
el.src = binding.value
stop()
}
},
)
},
})
<img v-lazy="imageUrl" alt="图片">
这种方式虽然比 loading="lazy"
要复杂,但是在实际企业开发中,这才是最可控的方式。
一句话总结下:如果是非常简单的页面,那么使用 loading="lazy"
确实是最快捷的方式;但如果你需要更多控制、更复杂的体验,IntersectionObserver
才是更强大的选择!
阅读原文:https://mp.weixin.qq.com/s/hc_qEthtOO_5OZrLuUgm8Q
该文章在 2025/9/16 12:26:21 编辑过