图片懒加载的常见实现方式
图片懒加载是一种优化网页性能的技术,它延迟加载页面中不可见的图片,直到用户滚动到它们的位置。这种技术可以显著减少初始页面加载时间、节省带宽并提高用户体验。以下是几种常见的图片懒加载实现方式:
1. 传统的滚动事件监听
实现原理:
- 初始时将图片的真实地址存储在自定义属性(如
data-src
)中,而不是src
属性 - 监听页面的
scroll
事件 - 当用户滚动页面时,检查图片是否进入视口
- 如果图片进入视口,将
data-src
的值赋给src
属性,触发图片加载
代码示例:
// 获取所有需要懒加载的图片
const lazyImages = document.querySelectorAll('img[data-src]');
// 滚动事件处理函数
function lazyLoad() {
lazyImages.forEach(img => {
if(isInViewport(img) && img.dataset.src) {
img.src = img.dataset.src;
img.removeAttribute('data-src');
}
});
}
// 检查元素是否在视口中
function isInViewport(element) {
const rect = element.getBoundingClientRect();
return (
rect.top >= 0 &&
rect.left >= 0 &&
rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
rect.right <= (window.innerWidth || document.documentElement.clientWidth)
);
}
// 添加滚动事件监听
window.addEventListener('scroll', lazyLoad);
// 初始加载
lazyLoad();
优缺点:
- 优点:兼容性好,实现简单
- 缺点:滚动事件频繁触发可能导致性能问题,需要进行节流处理
2. Intersection Observer API
实现原理:
- 使用现代浏览器提供的
Intersection Observer API
- 创建一个观察器,监控目标图片元素
- 当图片进入视口时,观察器的回调函数会被触发
- 在回调函数中加载图片
代码示例:
// 创建观察器实例
const observer = new IntersectionObserver((entries, observer) => {
entries.forEach(entry => {
// 当图片进入视口
if (entry.isIntersecting) {
const img = entry.target;
// 设置src属性,加载图片
img.src = img.dataset.src;
// 图片加载后,停止监听该元素
observer.unobserve(img);
}
});
});
// 获取所有懒加载图片并开始观察
document.querySelectorAll('img[data-src]').forEach(img => {
observer.observe(img);
});
优缺点:
- 优点:性能更好,不会阻塞主线程,代码更简洁
- 缺点:旧版浏览器兼容性问题(IE不支持)
3. 使用loading=“lazy”属性(原生懒加载)
实现原理:
- HTML5引入的原生懒加载属性
- 直接在
<img>
标签上添加loading="lazy"
属性 - 浏览器会自动处理懒加载逻辑
代码示例:
<img src="image.jpg" loading="lazy" alt="懒加载图片">
优缺点:
- 优点:实现极其简单,由浏览器原生支持,性能最优
- 缺点:较新的特性,旧版浏览器不支持
4. 使用第三方库
常见库:
- lazysizes
- lozad.js
- vanilla-lazyload
- react-lazyload(React应用)
- vue-lazyload(Vue应用)
示例(使用lazysizes):
<!-- 引入lazysizes库 -->
<script src="lazysizes.min.js"></script>
<!-- HTML结构 -->
<img data-src="image.jpg" class="lazyload" alt="懒加载图片">
优缺点:
- 优点:功能丰富,处理了各种边缘情况,兼容性好
- 缺点:增加了额外的依赖,可能增加页面体积
5. 渐进式加载(LQIP - Low Quality Image Placeholders)
实现原理:
- 先加载一个低质量、小尺寸的图片作为占位符
- 当用户滚动到图片位置时,再加载高质量大图
- 可以与上述任何一种懒加载技术结合使用
代码示例:
<img
src="tiny-image.jpg"
data-src="large-image.jpg"
class="lazyload"
alt="渐进式加载图片">
优缺点:
- 优点:提供更好的用户体验,避免布局偏移
- 缺点:需要为每张图片准备两个版本,增加了工作量
6. 使用CSS的background-image懒加载
实现原理:
- 对于作为背景的图片,可以通过动态添加类名来控制加载
- 初始时不设置
background-image
属性 - 当元素进入视口时,添加包含
background-image
的类
代码示例:
.lazy-background {
background-color: #f1f1f1; /* 占位背景色 */
}
.lazy-background.visible {
background-image: url('background.jpg');
}
const observer = new IntersectionObserver((entries, observer) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
entry.target.classList.add('visible');
observer.unobserve(entry.target);
}
});
});
document.querySelectorAll('.lazy-background').forEach(element => {
observer.observe(element);
});
7. 按需加载(On-demand Loading)
实现原理:
- 不使用传统的
<img>
标签,而是使用自定义元素或div - 当用户执行特定操作(如点击按钮)时才加载图片
- 适用于图库、轮播图等场景
代码示例:
document.getElementById('load-more-btn').addEventListener('click', () => {
const imageContainer = document.getElementById('image-container');
// 模拟获取更多图片
const newImages = ['image1.jpg', 'image2.jpg', 'image3.jpg'];
newImages.forEach(imgSrc => {
const img = new Image();
img.src = imgSrc;
img.alt = '按需加载的图片';
imageContainer.appendChild(img);
});
});
实际应用中的最佳实践
-
结合使用多种技术:
- 对于现代浏览器使用Intersection Observer
- 对于旧浏览器提供滚动事件监听作为降级方案
- 可能的情况下使用
loading="lazy"
属性
-
预加载关键图片:
- 首屏图片不应使用懒加载,应直接加载
- 只对首屏以下的图片应用懒加载
-
设置合理的阈值:
- 在图片即将进入视口前预先加载,避免用户看到空白
- 通常设置比视口大100-300px的预加载区域
-
处理加载失败情况:
- 提供加载失败的回退图片
- 实现重试机制
-
优化移动端体验:
- 考虑网络条件和设备性能
- 可能需要为移动设备提供更小的图片
总结
图片懒加载是提升网站性能的重要技术,选择哪种实现方式应根据项目需求、目标用户群体和浏览器兼容性要求来决定。在现代web开发中,Intersection Observer API和原生loading属性是最推荐的实现方式,但对于需要兼容旧浏览器的项目,传统的滚动事件监听或成熟的第三方库仍然是不错的选择。