前端性能优化实战:一文搞懂图片懒加载(Lazy Load),从原理到代码全解析

991 阅读4分钟

“打开网页半天没反应?”、“滚动时卡顿严重?”——如果你的网站充斥着大量高清图片,尤其是电商、资讯类站点,这些“视觉盛宴”背后往往藏着性能黑洞。

图片懒加载(Lazy Load),正是解决这一问题的利器。它能让你的网页加载速度提升 50% 以上,同时减少服务器压力和用户流量消耗。

本文将带你从零开始,深入理解懒加载的核心原理,手把手实现基础版与进阶版代码,并对比不同方案的优缺点,助你在项目中灵活应用!


🔍 一、什么是懒加载?为什么需要它?

1.1 什么是懒加载?

懒加载(Lazy Load),也叫延迟加载,是一种前端优化策略。其核心思想是:

只加载当前可视区域内的资源,其余资源在用户滚动到它们附近时再加载。

尤其适用于图片密集型页面,如电商首页、社交动态流、新闻列表等。

1.2 为什么要使用懒加载?

✅ 提升首屏加载速度

  • 页面首屏只需加载可视区图片,避免一次性下载所有图片。
  • 数据显示:页面加载每延迟 0.5 秒,跳出率上升 20%。

✅ 减少无效请求

  • 用户可能根本不会滚动到底部,提前加载只会浪费带宽和服务器资源。

✅ 降低并发压力

  • 浏览器默认并发请求数有限,过多图片会阻塞 CSS/JS 加载,导致白屏或交互延迟。

🧠 二、懒加载的核心原理

2.1 实现思路

  1. 占位图 + 自定义属性

    • 将真实图片地址存储在 data-src 中;
    • src 设置为小尺寸占位图(如 loading.gif 或透明 1x1 图片)。
  2. 监听滚动事件 / IntersectionObserver

    • 判断图片是否进入可视区域;
    • 若进入,则将 src 替换为 data-src 的值,触发加载。
  3. 优化体验细节

    • 使用节流函数控制滚动频率;
    • 预加载即将进入视口的图片;
    • 失败处理、缓存机制等。

2.2 关键知识点回顾

技术点描述
data-* 属性存储自定义数据,用于保存真实图片地址
getBoundingClientRect()获取元素相对于视口的位置
window.addEventListener('scroll')监听滚动事件判断图片是否可见
Image() 构造函数创建临时图片对象预加载资源
IntersectionObserver API现代浏览器推荐使用的异步监听方式

🛠️ 三、从零实现一个懒加载功能

3.1 HTML 结构

<img class="lazy" src="loading.gif" data-src="aw.jpg" />
<img class="lazy" src="loading.gif" data-src="aw.jpg" />
<!-- 更多图片... -->

3.2 JavaScript 实现(基础版)

function lazyload() {
  const images = document.querySelectorAll('img[data-src]');
  const viewHeight = window.innerHeight;

  images.forEach(img => {
    if (img.dataset.src === '') return;
    const rect = img.getBoundingClientRect();
    if (rect.top < viewHeight && rect.bottom > 0) {
      const realSrc = img.dataset.src;
      const tempImg = new Image();
      tempImg.onload = () => {
        img.src = realSrc;
        img.removeAttribute('data-src');
      };
      tempImg.src = realSrc;
    }
  });
}

// 初始化加载 + 滚动监听
window.addEventListener('DOMContentLoaded', lazyload);
window.addEventListener('scroll', lazyload);

3.3 性能优化建议

  • ✅ 使用节流函数减少频繁调用:
function throttle(fn, delay) {
  let timer = null;
  return function () {
    if (!timer) {
      timer = setTimeout(() => {
        fn.apply(this, arguments);
        timer = null;
      }, delay);
    }
  };
}
window.addEventListener('scroll', throttle(lazyload, 100));
  • ✅ 使用 IntersectionObserver 替代 scroll 监听:
const observer = new IntersectionObserver((entries) => {
  entries.forEach(entry => {
    if (entry.isIntersecting) {
      const img = entry.target;
      img.src = img.dataset.src;
      img.removeAttribute('data-src');
      observer.unobserve(img); // 可选:加载完停止观察
    }
  });
});

document.querySelectorAll('img[data-src]').forEach(img => observer.observe(img));

⚙️ 四、进阶技巧与注意事项

4.1 占位图设计

  • 推荐使用固定大小的占位图,防止布局抖动;
  • 可使用 Base64 编码的小图内联减少 HTTP 请求。

如果不懂Base64,可以看看这篇文章前端高手才知道的秘密:Blob 居然这么强大!你是否想过,网页中那些炫酷的图片、文件下载、本地预览功能背后,隐藏着怎样的 - 掘金

4.2 预加载“未来”图片

通过 rootMargin 提前加载即将进入视口的图片:

new IntersectionObserver(callback, {
  rootMargin: '0px 0px 200px 0px' // 距离底部 200px 就开始加载
});

4.3 错误处理

tempImg.onerror = () => {
  img.src = 'default.png'; // 加载失败备用图
};

4.4 兼容性处理

  • IE 不支持 IntersectionObserver,可使用 polyfill 或回退到 scroll + getBoundingClientRect 方案。

📊 五、懒加载 vs 预加载:谁更适合你?

对比维度懒加载(Lazy Load)预加载(Preload)
适用场景图片多、页面长、非关键路径资源首屏关键资源、动画序列图
加载时机滚动到可视区后加载页面加载前就预加载
优点首屏快、节省流量、减轻服务器压力后续操作流畅无等待
缺点需要监听和判断逻辑,用户体验略差初次加载慢,占用更多带宽

📌 六、总结

图片懒加载是一项简单但效果显著的前端性能优化手段。掌握它的原理和实现方式,不仅能提升用户体验,还能减少服务器压力,在电商、社区、新闻等高并发场景中尤为重要。

✅ 本文重点回顾:

  • 懒加载本质是“按需加载”,避免一次性加载所有图片;
  • 使用 data-srcsrc 分离真实地址与占位图;
  • 基础实现依赖滚动监听 + getBoundingClientRect()
  • 进阶方案使用 IntersectionObserver,更高效且兼容现代浏览器;
  • 注意节流、错误处理、预加载等优化技巧。

扩展学习: MDN - IntersectionObserver