图片懒加载的常见实现方式

图片懒加载是一种优化网页性能的技术,它延迟加载页面中不可见的图片,直到用户滚动到它们的位置。这种技术可以显著减少初始页面加载时间、节省带宽并提高用户体验。以下是几种常见的图片懒加载实现方式:

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);
  });
});

实际应用中的最佳实践

  1. 结合使用多种技术

    • 对于现代浏览器使用Intersection Observer
    • 对于旧浏览器提供滚动事件监听作为降级方案
    • 可能的情况下使用loading="lazy"属性
  2. 预加载关键图片

    • 首屏图片不应使用懒加载,应直接加载
    • 只对首屏以下的图片应用懒加载
  3. 设置合理的阈值

    • 在图片即将进入视口前预先加载,避免用户看到空白
    • 通常设置比视口大100-300px的预加载区域
  4. 处理加载失败情况

    • 提供加载失败的回退图片
    • 实现重试机制
  5. 优化移动端体验

    • 考虑网络条件和设备性能
    • 可能需要为移动设备提供更小的图片

总结

图片懒加载是提升网站性能的重要技术,选择哪种实现方式应根据项目需求、目标用户群体和浏览器兼容性要求来决定。在现代web开发中,Intersection Observer API和原生loading属性是最推荐的实现方式,但对于需要兼容旧浏览器的项目,传统的滚动事件监听或成熟的第三方库仍然是不错的选择。