图片懒加载:让网页加载「偷懒」又高效的前端技巧

220 阅读6分钟

一、为什么需要图片懒加载?—— 从浏览器加载机制说起

(一)浏览器的「急性子」:图片加载的「堵车现场」

当网页中存在大量图片(如电商网站的 50 + 商品图),浏览器会通过src属性立即发起 HTTP 请求。但浏览器的下载线程有限(不同浏览器并发策略不同,通常 2-10 个),就像狭窄公路突然涌入大量车辆,网络带宽瞬间被抢占,导致首屏加载缓慢,甚至出现「页面卡死」的尴尬局面~

专业简述:浏览器解析 HTML 时,遇到<img src="url">会立即触发网络请求,遵循 HTTP 协议从服务器获取资源。但 TCP/IP 并发连接有限,大量图片同时加载会阻塞关键资源,影响用户体验。

(二)懒加载的核心思想:「按需加载」才是硬道理

懒加载(Lazy Load)的本质是「延迟加载非可视区图片」—— 当图片进入浏览器视口或即将进入时,再触发真实图片的加载。就像外卖小哥不会一次性送完所有订单,而是按用户下单顺序分批配送,既节省带宽又提升效率~

专业简述:通过监听滚动事件或浏览器原生 API,判断图片是否在可视区域内,仅加载用户实际能看到的图片,减少初始加载时的网络请求量。

二、图片懒加载原理全解析 —— 如何让图片「该出现时再出现」

(一)可视区检测:给图片装个「入场感应器」

  1. getBoundingClientRect() :获取图片相对于视口的位置,判断top < 视口高度 && bottom > 0,即认为图片进入可视区。
    专业简述:该方法返回元素的位置信息(左、右、上、下边界),通过对比视口高度(window.innerHeight),确定是否触发加载逻辑。
  2. Intersection Observer API(现代浏览器首选):异步监听元素与视口的交集变化,无需频繁计算,性能更优。
    专业简述:创建观察者对象,设定阈值(如 10% 进入视口即触发),当图片与视口产生交集时,自动执行加载回调。

(二)占位图与自定义属性:「替身」与「真实身份」的巧妙切换

  1. 占位图(Placeholder) :初始时src设置为小尺寸加载图(如 1x1 像素透明图或加载动画),避免空白区域影响布局,同时减少首次请求体积。
    专业简述:占位图通常缓存于本地或 CDN,仅需请求一次,确保页面结构稳定,提升用户体验。
  2. *data-自定义属性:用data-original存储真实图片地址,避免初始加载时触发请求。当图片进入可视区,通过 JS 将data-original的值赋给src,完成「替身换真」。
    专业简述:利用 HTML5 的数据属性规范,解耦图片的展示与加载逻辑,确保初始渲染时不发起无效请求。

三、从 0 到 1 实现图片懒加载 —— 代码实战与最佳实践

(一)基础实现:传统滚动监听方案(兼容旧浏览器)

<!-- HTML结构:占位图+真实地址存储 -->
<img class="lazy" 
     src="https://static.360buyimg.com/item/main/1.0.12/css/i/loading.gif"  <!-- 占位图 -->
     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"  <!-- 真实图片地址 -->
     alt="示例图">
// 懒加载核心逻辑
function lazyLoad() {
  // 获取所有带懒加载标记的图片
  const lazyImgs = document.querySelectorAll('img[data-original][lazyload]');
  // 获取视口高度(浏览器可见区域高度)
  const viewportHeight = window.innerHeight;
  
  lazyImgs.forEach(img => {
    // 跳过已加载的图片(无真实地址时直接返回)
    if (!img.dataset.original) return;
    
    // 获取图片相对于视口的位置信息
    const rect = img.getBoundingClientRect();
    
    // 当图片底部进入视口、顶部未完全离开视口时,触发加载
    if (rect.bottom >= 0 && rect.top < viewportHeight) {
      // 动态创建img对象预加载真实图片
      const tempImg = new Image();
      tempImg.src = img.dataset.original;
      
      // 预加载完成后替换src,避免直接赋值导致的闪烁
      tempImg.onload = function() {
        img.src = img.dataset.original; // 替换为真实图片
        img.removeAttribute('data-original'); // 移除标记,避免重复加载
        img.removeAttribute('lazyload');
      }
    }
  });
}

// 监听滚动事件(页面滚动时触发懒加载检测)
window.addEventListener('scroll', lazyLoad);
// 页面初始加载时检测一次(首屏图片立即加载)
document.addEventListener('DOMContentLoaded', lazyLoad);

(二)进阶优化:Intersection Observer API 高效实现

现代浏览器推荐使用此方案,无需频繁计算元素位置,性能更优~

function initLazyLoad() {
  // 创建观察者实例,配置观察规则
  const observer = new IntersectionObserver((entries) => {
    entries.forEach(entry => {
      // 当图片进入视口(交集比例满足阈值)
      if (entry.isIntersecting) {
        const img = entry.target;
        // 预加载真实图片
        const tempImg = new Image();
        tempImg.src = img.dataset.original;
        
        tempImg.onload = function() {
          img.src = img.dataset.original;
          img.removeAttribute('data-original');
          img.removeAttribute('lazyload');
        }
        
        // 加载完成后停止观察(节省性能)
        observer.unobserve(img);
      }
    });
  }, {
    rootMargin: '200px 0px' // 提前200px开始加载,避免滚动时白屏
  });
  
  // 监听所有带懒加载标记的图片
  document.querySelectorAll('img[data-original][lazyload]').forEach(img => {
    observer.observe(img);
  });
}

// 页面加载完成后初始化
document.addEventListener('DOMContentLoaded', initLazyLoad);

(三)极简方案:浏览器原生 lazy 属性(适合现代项目)

<!-- 一行代码实现懒加载,浏览器自动处理加载时机 -->
<img src="https://static.360buyimg.com/item/main/1.0.12/css/i/loading.gif"
     data-original="https://img.36krcdn.com/hsossms/20250312/v2_aeaa7a1d51e74c3a8f909c96cd73a687@000000_oswg169950oswg1440oswg600_img_jpeg?x-oss-process=image/format,webp"
     loading="lazy"  <!-- 原生懒加载属性 -->
     alt="示例图">

注意:兼容性需考虑(Chrome 77+、Firefox 75 + 支持),旧浏览器会降级为普通加载,建议搭配 JS 方案做渐进增强。

四、避坑指南与性能优化 —— 让懒加载「稳如老狗」

(一)常见问题解决方案

  1. 图片加载时布局抖动

    • 提前设置图片宽高(width/height)或用 CSS 固定比例:

      .image-item {
        width: 100%;
        height: auto;
        aspect-ratio: 16/9; /* 保持宽高比,避免布局偏移 */
      }
      
  2. 低网速下图片闪烁

    • 占位图使用与真实图片同尺寸的模糊缩略图(如通过 OSS 生成的低清版本),加载完成后平滑过渡。

(二)性能优化技巧

优化方向具体方案
减少无效计算Intersection Observer替代滚动监听,避免高频触发getBoundingClientRect()
提前加载缓冲设置rootMargin: '200px 0',让图片在距离视口 200px 时开始加载
图片格式优化真实图片优先使用 WebP/AVIF 格式,占位图用 1x1 像素透明 PNG(体积 < 1KB)
首屏图片特殊处理首屏可见图片不添加懒加载标记,直接通过src加载,避免延迟显示

五、总结:懒加载是前端性能优化的「性价比之王」

图片懒加载通过「按需加载」的核心逻辑,在不影响用户体验的前提下,能减少 50% 以上的初始网络请求,尤其适合电商、图库、长文章等图片密集型场景。无论是传统滚动监听、现代 API 还是原生属性,核心都是「让合适的图片在合适的时机出现」。

掌握懒加载不仅能提升网页加载速度,更能体现对用户体验的细节把控 —— 毕竟,没人愿意等一个加载半天的网页,你说对吧~