一、什么是图片懒加载
让我们问问豆包,它是这么回答的:
一言蔽之,图片懒加载是一种网页性能优化技术,核心目的是减少页面初始加载时的资源请求量,让图片在用户真正需要看到它时才进行加载,从而提升页面加载速度和用户体验。
二、为什么需要图片懒加载
所以为什么需要图片懒加载呢?让我们将继续追问豆包:
相较于传统加载的一次性端上所有菜,懒加载则是吃一道上一道,这样的好处是你不用等所有图片都加载完,能更快看到内容,还不浪费流量(没看到的图片不提前加载)。
三、如何实现图片懒加载
首先我们需要解决图片提前加载的矛盾,因为如果直接在<img src>中写真实图片地址,浏览器会立即发起请求,违背懒加载 “按需加载” 的原则。
这该如何解决呢?我们可以用自定义属性(如 data-original) 存储真实图片 URL,src会先赋值为占位符(小尺寸透明图或低清图) ,仅在图片进入可视区域时才将data-original的值赋给src。
<img
class="image-item"
lazyload="true"
src="预加载图片地址"
data-original="真实图片地址"
/>
这样当页面打开时,会先显示占位图(不影响布局),当用户滚动到该商品时,JS触发加载真实商品图,提升浏览流畅度。
而懒加载的核心逻辑是:先不加载所有图片,等图片出现在浏览器可视区域时,再偷偷加载它。
要实现图片懒加载需要判断<img>标签是否出现在可视区域,还需要考虑浏览器窗口的大小以及页面的滚动区域。
这里我们可以用getBoundingClientRect ()方法
这个方法是判断元素是否在可视区域的关键,它返回一个 DOMRect 对象,包含元素相对于浏览器视口的位置信息。
const rect = element.getBoundingClientRect();
// 返回的rect对象包含:
{
top: 100, // 元素顶部到视口顶部的距离(px)
bottom: 300, // 元素底部到视口顶部的距离(px)
left: 50, // 元素左侧到视口左侧的距离(px)
right: 250, // 元素右侧到视口左侧的距离(px)
width: 200, // 元素宽度(px)
height: 200, // 元素高度(px)
x: 50, // 等同于left
y: 100 // 等同于top
}
设置核心判断条件:元素顶部低于视口顶部,且元素底部高于视口底部。
// 获取视口高度
const viewportHeight = window.innerHeight;
// 判断元素是否在视口内
function isInViewport(element) {
const rect = element.getBoundingClientRect();
return (
rect.top <= viewportHeight && // 元素顶部在视口内或视口上方
rect.bottom >= 0 // 元素底部在视口内或视口下方
);
}
整体代码实现如下
// 1. 获取视口高度
const viewportHeight = window.innerHeight;
// 2. 获取所有需要懒加载的图片(通过自定义属性标记)
const lazyImages = document.querySelectorAll('img[data-src]');
// 3. 定义可视区域判断函数
function isInViewport(element) {
const rect = element.getBoundingClientRect();
// 核心判断条件:元素顶部低于视口底部,且元素底部高于视口顶部
return (
rect.top <= viewportHeight && // 元素顶部在视口内或视口上方
rect.bottom >= 0 // 元素底部在视口内或视口下方
);
}
// 4. 懒加载执行函数
function loadLazyImages() {
lazyImages.forEach(img => {
// 若图片已加载或无真实地址,跳过
if (img.src === img.dataset.src || !img.dataset.src) return;
// 检查图片是否在可视区域
if (isInViewport(img)) {
// 创建临时Image对象预加载图片(避免页面闪烁)
const tempImg = new Image();
tempImg.src = img.dataset.src;
// 图片加载完成后替换src,并清理属性
tempImg.onload = () => {
img.src = tempImg.src;
img.removeAttribute('data-src'); // 移除自定义属性
};
// 处理加载失败
tempImg.onerror = () => {
img.src = 'https://via.placeholder.com/100x100?text=加载失败';
};
}
});
}
// 5. 初始加载与滚动监听
loadLazyImages(); // 加载首屏图片
window.addEventListener('scroll', loadLazyImages); // 滚动时检查新图片
代码执行流程
-
初始状态:
- 图片
src为占位图(小尺寸加载快),真实地址存于data-src; - 浏览器解析 HTML 后,首屏图片通过
loadLazyImages()检查并加载。
- 图片
-
滚动场景:
- 用户滚动页面时,
scroll事件触发loadLazyImages(); - 函数遍历所有图片,用
getBoundingClientRect()判断位置; - 符合条件的图片通过
new Image()预加载,完成后替换src。
- 用户滚动页面时,
-
优化效果:
- 仅加载可视区域图片,减少初始请求量;
- 预加载机制确保图片显示时已缓存,避免闪烁。
四、性能优化
示例代码存在几个缺点:
滚动事件高频触发:未使用节流 / 防抖,每秒可触发 60 次计算,导致浏览器卡顿
重复遍历图片:已加载图片仍被反复判断,浪费 CPU 资源
我们可以添加节流处理(避免频繁滚动触发):
function throttle(func, delay) {
let timer;
return function() {
if (timer) return;
timer = setTimeout(() => {
func.apply(this, arguments);
timer = null;
}, delay);
};
}
window.addEventListener('scroll', throttle(loadLazyImages, 200)); // 200ms执行一次
核心作用:限制函数在指定时间内的执行频率,避免高频触发导致性能问题。
The End
以上内容就是关于图片懒加载的实现。