现代浏览器原生支持loading="lazy"属性实现图片懒加载,无需JS;兼容性不足或特殊场景(如transform容器、占位图动画)时应使用IntersectionObserver;框架项目优先选用成熟方案,background-image懒加载必须依赖JS。

图片懒加载不是靠 setTimeout 或滚动监听手动计算位置来“模拟”的,现代浏览器原生支持的 loading="lazy" 属性已经足够可靠,且无需 JS 就能生效——但要注意兼容性与特殊场景下的 fallback。
什么时候直接用 loading="lazy" 就够了
绝大多数静态图片、内容页中的普通 标签,只要满足以下条件,加一个属性就行:
- 图片在视口下方(非首屏),且父容器没有
overflow: hidden或transform等触发新层叠上下文的样式(否则 Chrome 可能失效) - 目标浏览器是 Chrome 76+、Edge 79+、Firefox 75+、Safari 15.4+;iOS Safari 15.4+ 才开始支持
loading,旧版需降级 - 不依赖图片加载后的尺寸回填(
loading="lazy"不触发onload事件的提前调用,也不影响 CSS aspect-ratio 占位)
示例:
IntersectionObserver 是唯一靠谱的手动方案
当需要兼容老浏览器、或图片被包裹在 transform 容器里、或要配合占位图/渐入动画时,必须用 IntersectionObserver。它比 scroll 事件监听性能好得多,且能精确控制阈值。
一套面向小企业用户的企业网站程序!功能简单,操作简单。实现了小企业网站的很多实用的功能,如文章新闻模块、图片展示、产品列表以及小型的下载功能,还同时增加了邮件订阅等相应模块。公告,友情链接等这些通用功能本程序也同样都集成了!同时本程序引入了模块功能,只要在系统默认模板上创建模块,可以在任何一个语言环境(或任意风格)的适当位置进行使用!
立即学习“Java免费学习笔记(深入)”;
- 不要监听
window.onscroll:频繁触发 + 强制同步布局导致卡顿 - 创建 observer 时设
rootMargin: '0px 0px 200px 0px',让图片在进入视口前 200px 就开始加载,避免滚动快时白屏 - 每个
img的src应先存为data-src,observer 触发后才赋给src,并移除自身监听(unobserve) - 务必检查
isIntersecting,避免重复加载(尤其在页面缩放或 resize 后可能重触发)
示例关键逻辑:
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const img = entry.target;
img.src = img.dataset.src;
observer.unobserve(img);
}
});
}, { rootMargin: '0px 0px 200px 0px' });
Vue/React 项目里别自己封装懒加载组件
框架生态已有成熟方案,重复实现容易踩坑:
- Vue:优先用
v-lazy(vue-lazyload)或组合式 API +useIntersectionObserver(@vueuse/core),避免在mounted里漏掉动态插入的图片 - React:用
react-intersection-observer,别在useEffect里反复 new observer;SSR 时注意服务端不执行,需设默认占位或fallback - 所有框架中,
background-image懒加载必须靠 JS(CSS 不支持 lazy),且无法用loading="lazy",得走IntersectionObserver+style.backgroundImage
真正麻烦的从来不是“怎么写”,而是图片尺寸不稳定导致布局抖动、第三方 SDK(如广告、统计)注入的图片绕过你的 observer、以及 iOS Safari 在某些 zoom 级别下 IntersectionObserver 回调延迟——这些都得靠真实设备测试,不能只看桌面 Chrome。










