图片懒加载指的是当页面滚动到图片所在可视区域时,才加载图片资源,避免首屏一次性加载所有图片导致页面加载缓慢、浪费用户流量的问题。该方案能有效提升首屏加载速度,降低服务器请求压力。

图片懒加载的核心原理
图片懒加载的核心逻辑是暂时不将图片的真实地址赋值给<img>标签的src属性,而是把真实地址存储在其他自定义属性中(通常使用data-src)。当页面滚动时,判断<img>标签是否进入浏览器的可视区域,如果进入就将data-src的值赋值给src属性,触发图片加载。
传统实现方案:基于滚动事件判断
传统方案通过监听window的scroll事件,结合DOM元素的位置信息和可视区域高度来判断图片是否进入可视区。需要用到以下三个关键属性:
- offsetTop:元素相对于其offsetParent顶部的距离
- scrollTop:页面滚动的距离,即文档顶部被卷去的高度
- clientHeight:浏览器可视区域的高度
判断逻辑为:当元素的offsetTop小于等于scrollTop加上clientHeight时,说明元素已经进入可视区域。
传统方案实现代码
// 获取所有需要懒加载的图片
const lazyImages = document.querySelectorAll('img[data-src]');
// 判断图片是否进入可视区的函数
function isInViewport(img) {
const rect = img.getBoundingClientRect();
// getBoundingClientRect返回元素相对于可视区域的位置信息
return rect.top <= window.innerHeight && rect.bottom >= 0;
}
// 加载图片的函数
function loadImage(img) {
const src = img.getAttribute('data-src');
if (src) {
img.src = src;
// 加载完成后移除data-src属性,避免重复处理
img.removeAttribute('data-src');
}
}
// 处理懒加载的函数
function handleLazyLoad() {
lazyImages.forEach(img => {
if (isInViewport(img)) {
loadImage(img);
}
});
}
// 初始加载时先执行一次,加载首屏可视区的图片
handleLazyLoad();
// 监听滚动事件
window.addEventListener('scroll', handleLazyLoad);
这种方案需要注意滚动事件触发频率很高,可能会导致性能问题,通常需要搭配节流函数优化:
// 节流函数,限制函数在一定时间内只执行一次
function throttle(fn, delay) {
let timer = null;
return function() {
if (!timer) {
timer = setTimeout(() => {
fn.apply(this, arguments);
timer = null;
}, delay);
}
};
}
// 使用节流后的滚动监听
window.addEventListener('scroll', throttle(handleLazyLoad, 200));
现代实现方案:Intersection Observer API
Intersection Observer API是浏览器提供的原生接口,用于异步观察目标元素与祖先元素或顶级文档视口的交叉状态,不需要手动计算位置,性能更优,代码更简洁。
Intersection Observer实现代码
// 获取所有需要懒加载的图片
const lazyImages = document.querySelectorAll('img[data-src]');
// 创建观察器实例
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
// 判断元素是否进入可视区
if (entry.isIntersecting) {
const img = entry.target;
const src = img.getAttribute('data-src');
if (src) {
img.src = src;
img.removeAttribute('data-src');
}
// 图片加载完成后停止观察该元素
observer.unobserve(img);
}
});
}, {
// 提前50px触发加载,提升用户体验
rootMargin: '0px 0px 50px 0px'
});
// 遍历所有图片,开始观察
lazyImages.forEach(img => {
observer.observe(img);
});
两种方案对比
两种实现方案的特点对比如下:
| 方案类型 | 优势 | 劣势 | 适用场景 |
|---|---|---|---|
| 滚动事件方案 | 兼容性更好,支持更多老版本浏览器 | 需要手动计算位置,滚动事件频繁需要节流,性能相对较差 | 需要兼容老版本浏览器的项目 |
| Intersection Observer方案 | 原生API支持,无需手动计算,性能优异,代码简洁 | 兼容性稍差,不支持IE等老版本浏览器 | 面向现代浏览器的项目 |
注意事项
- 图片的<img>标签初始src可以设置为占位图或者空值,避免浏览器发送无效请求
- 如果页面存在动态新增的图片,需要给新增的图片也添加data-src属性,并重新执行观察逻辑
- 使用Intersection Observer时,可以通过rootMargin参数调整触发加载的提前量,避免用户滚动到图片位置时才开始加载导致等待
图片懒加载javascriptIntersection_Observerdata_src性能优化修改时间:2026-06-21 12:06:17