图像懒加载实现指南 - loading=lazy 与 IntersectionObserver 的选择
为什么需要图像懒加载
懒加载延迟加载视口外的图像,仅在用户即将看到时才开始加载。这显著减少初始页面加载时间和带宽消耗。
收益:
- 减少初始加载量:首屏外的图像不在页面加载时请求,加速 DOMContentLoaded 和 LCP
- 节省带宽:用户可能不会滚动到页面底部,未查看的图像无需加载
- 减少并发请求:避免数十张图像同时请求导致的带宽竞争
注意:首屏图像(Above the fold)不应懒加载,否则会延迟 LCP。懒加载仅用于首屏以下的图像。
原生 loading=lazy - 最简单的实现
HTML 原生的 loading="lazy" 属性是最简单的懒加载实现,无需 JavaScript。
用法:<img src="photo.jpg" loading="lazy" alt="描述" width="800" height="600">
浏览器行为:
- 浏览器自动判断图像是否在视口附近,决定何时开始加载
- 具体的「提前加载距离」由浏览器决定(Chrome 约 1250px-2500px,取决于网络速度)
- 不可自定义触发距离
优势:
- 零 JavaScript,最简单的实现
- 浏览器原生优化,性能最好
- 自动处理各种边缘情况(打印、搜索引擎爬虫等)
局限:
- 无法自定义触发距离(rootMargin)
- 无法添加加载动画或占位效果
- 无法实现渐进式加载(LQIP → 全图)
- 必须设置 width/height 防止 CLS
IntersectionObserver - 灵活的自定义方案
IntersectionObserver API 提供高性能的元素可见性检测,可实现完全自定义的懒加载逻辑。
基本实现:
- 图像初始不设置 src,将真实 URL 存在
data-src属性 - 创建 IntersectionObserver 监听图像元素
- 元素进入视口时,将
data-src赋值给src触发加载 - 加载完成后取消观察该元素
自定义选项:
rootMargin: "200px":提前 200px 开始加载(预加载缓冲区)threshold: 0:元素刚进入视口即触发(默认)- 可配合加载动画:src 赋值前显示骨架屏,加载完成后淡入
优势:完全可控的触发时机、可添加加载动画、支持 LQIP 渐进加载、可实现优先级控制。
两种方案的对比与选择
根据项目需求选择合适的懒加载方案,或组合使用。
对比:
- 实现复杂度:loading=lazy(零代码)vs IO(需要 JS 实现)
- 可定制性:loading=lazy(不可定制)vs IO(完全可定制)
- 加载动画:loading=lazy(无)vs IO(可自定义)
- 触发距离:loading=lazy(浏览器决定)vs IO(rootMargin 自定义)
- 兼容性:两者都有良好的现代浏览器支持
推荐策略:
- 简单博客/文档站:直接使用 loading=lazy,零成本获得懒加载
- 图片密集型应用:IntersectionObserver + LQIP + 加载动画
- 电商产品列表:IO + 优先级控制(首屏产品优先加载)
防止布局偏移(CLS)的最佳实践
懒加载图像如果没有预留空间,加载完成时会导致页面布局偏移(CLS),严重影响用户体验和 Core Web Vitals 分数。
解决方案:
- 明确 width/height:始终在 img 标签上设置宽高属性,浏览器据此预留空间
- aspect-ratio CSS:
img { aspect-ratio: 16/9; width: 100%; height: auto; } - 容器占位:用固定宽高比的容器包裹图像,图像用 object-fit 填充
LQIP(低质量占位图):
- 内联极小的模糊缩略图(如 20x15px 的 Base64)作为占位
- 全图加载完成后替换占位图,使用 CSS 过渡实现平滑切换
- 占位图体积 < 1KB,可内联在 HTML 中无需额外请求
框架集成与高级模式
主流前端框架都提供了图像懒加载的内置支持或推荐方案。
Next.js Image:
<Image>组件默认启用懒加载- 自动生成 srcset 和 sizes
- 内置 blur placeholder 支持
- 首屏图像设置
priority属性跳过懒加载
React:
- 自定义 Hook:
useIntersectionObserver封装懒加载逻辑 - 库:
react-lazy-load-image-component
Vue:
v-lazy指令(vue-lazyload 库)- Nuxt Image 组件内置懒加载
高级模式:
- 优先级队列:根据图像在视口中的位置排序加载优先级
- 网络感知:慢速网络时加载更小的图像或降低预加载距离
- 空闲时预加载:使用
requestIdleCallback在空闲时预加载即将可见的图像