图片懒加载:从基础到进阶的实现与优化

130 阅读4分钟

前言:

你是否遇到过这样的情况:打开一个图片很多的网页时,半天都加载不出来,进度条慢悠悠地爬?这是因为传统网页会一股脑加载所有图片,不管你看不看得到。而懒加载技术就像一个聪明的管家,只在你需要的时候才把图片呈现出来。今天我们就来聊聊这个提升网页性能的小技巧,看看它是如何让网页加载速度飞起来的。

什么是懒加载?为什么需要它?

上面提到的——当我们打开图片密集型网页时,加载半天还在转圈圈的情况,这是因为传统网页会一次性加载所有图片资源,导致:

  • 首屏加载缓慢,用户体验差
  • 浪费带宽(尤其对移动用户)
  • 服务器压力增大

懒加载 的核心思想是: 只加载用户当前可见区域的图片 ,滚动到哪里就加载到哪里,从根源上解决上述问题。

接下来让我们看看懒加载的具体实现。

实现原理:从传统方案到现代API

一、传统方案:监听滚动事件(基于scroll)

传统懒加载通过监听 scroll 事件,在用户滚动页面时动态检查图片是否进入视口,从而触发加载。核心思路是: 用占位符图片代替真实图片地址,通过自定义属性存储真实URL,在图片进入视口时替换src属性 。

<!-- HTML结构 -->
<img class="image-item"
     lazyload="true"
     src="占位图地址"
     data-original="真实图片地址"/>

<script>
  // 1. 获取视窗高度
  const viewHeight = document.documentElement.clientHeight;
  
  // 2. 选择所有需要懒加载的图片
  const eles = document.querySelectorAll("img[data-original][lazyload]");
  
  // 3. 核心懒加载逻辑
  const lazyLoad = function () {
    Array.prototype.forEach.call(eles, function (item) {
      // 跳过已加载的图片
      if (item.dataset.original === "") return;
      
      // 获取元素位置信息
      const rect = item.getBoundingClientRect();
      
      // 4. 判断是否进入视口
      if (rect.top < viewHeight && rect.bottom > 0) {
        // 5. 预加载图片
        (function () {
          var img = new Image();
          img.src = item.dataset.original;
          img.onload = function () {
            // 加载完成后替换src
            item.src = item.dataset.original;
            // 移除标记属性
            item.removeAttribute("lazyload");
          };
        })();
      }
    });
  };
  
  // 6. 绑定事件监听
  window.addEventListener("scroll", lazyLoad);
  document.addEventListener("DOMContentLoaded", lazyLoad);
</script>

关键技术点解析

  1. 占位符与真实地址分离

    • src 属性使用空白图片或loading动画(如 blank.png )
    • data-original 存储真实图片URL,避免浏览器自动加载
  2. 视口判断逻辑

// 元素顶部进入视口且底部未完全离开视口
if (rect.top < viewHeight && rect.bottom > 0)

通过 getBoundingClientRect() 获取元素相对于视口的位置, rect.top 是元素顶部到视口顶部的距离

  1. 图片预加载优化 使用 new Image() 创建图片对象进行预加载,在 onload 回调中才替换 src ,避免加载过程中出现空白

  2. 事件触发时机

    • DOMContentLoaded :页面初始加载时检查首屏图片
    • scroll :滚动过程中实时检查

二、现代方案:IntersectionObserver API(推荐)

IntersectionObserver是浏览器提供的异步观察元素交叉状态的API,通过监听目标元素与视口的交叉情况自动触发回调,无需手动监听scroll事件。核心优势在于 异步执行 和 自动性能优化 ,解决了传统方案的性能瓶颈。

<ul id="list">
  <li class="list-item">
    <img 
      src="" 
      class="list-item-img" 
      lazyload="true" 
      data-original="https://img.36krcdn.com/hsossms/20250313/v2_15ad8ef9eca34830b4a2e081bbc7f57a@000000_oswg172644oswg1536oswg722_img_000?x-oss-process=image/resize,m_mfit,w_960,h_400,limit_0/crop,w_960,h_400,g_center" 
    />
  </li>
  <!-- 更多图片... -->
</ul>
<script>
function addObserver() {
  // 1. 选择所有需要懒加载的图片
  const eles = document.querySelectorAll('img[data-original][lazyload]');

  // 2. 创建IntersectionObserver实例
  const observer = new IntersectionObserver(function(changes) {
    // 3. 交叉状态变化时触发的回调
    changes.forEach(function(element) {
      // 4. 判断元素是否进入视口
      if (element.intersectionRatio > 0 && element.intersectionRatio <= 1) {
        // 5. 预加载图片
        const img = new Image();
        img.src = element.target.dataset.original;
        img.onload = function() {
          // 加载完成后替换src
          element.target.src = img.src;
        };
        // 6. 停止观察已加载的图片
        observer.unobserve(element.target);
      }
    });
  });

  // 7. 开始观察所有目标元素
  eles.forEach((item) => observer.observe(item));
}

// 页面加载后初始化
addObserver();
</script>

关键技术点解析

  1. IntersectionObserver构造函数
const observer = new IntersectionObserver(callback(){});
// callback 是元素交叉状态变化时的回调函数
  1. 交叉状态判断
if (element.intersectionRatio > 0 && element.intersectionRatio <= 1)
  • intersectionRatio :元素可见比例(0~1),>0表示进入视口
  • 相比传统方案的 getBoundingClientRect() ,此判断完全由浏览器优化执行
  1. 自动停止观察 observer.unobserve(element.target): 加载完成后停止观察,避免重复触发,优化性能
  2. 批量观察机制 eles.forEach((item) => observer.observe(item)):一次性对所有图片元素进行观察,代码更简洁

两种方案对比表

特性传统scroll方案IntersectionObserver方案
兼容性所有浏览器IE不支持,现代浏览器支持
性能较差(需手动优化)优秀(浏览器原生优化)
代码复杂度较高(需处理位置计算)低(API封装完善)
触发频率极高(需节流)按需触发(只在可见性变化时)
额外依赖旧浏览器需polyfill

实际项目建议: 新项目直接使用 IntersectionObserver ,简洁高效。

如果需要兼容旧浏览器 :主方案的话仍然是使用 IntersectionObserver,降级方案——传统scroll监听(通过特性检测实现)不过传统方案在 性能优化方面 需配合 节流

总结

图片懒加载是前端性能优化的基础手段,从早期的scroll监听发展到现在的IntersectionObserver API,实现方式越来越优雅。建议在实际项目中优先采用IntersectionObserver方案,兼顾性能和开发效率。