图片懒加载

文章目录

  • [图片懒加载 前端核心笔记](#图片懒加载 前端核心笔记)
    • 一、什么是图片懒加载
    • 二、实现原理
      • [1. 核心前提](#1. 核心前提)
      • [2. 基础实现步骤](#2. 基础实现步骤)
    • 三、具体实现方式
      • [方式1:原生 IntersectionObserver(推荐,性能最优)](#方式1:原生 IntersectionObserver(推荐,性能最优))
      • [方式2:scroll 事件 + getBoundingClientRect(兼容旧浏览器)](#方式2:scroll 事件 + getBoundingClientRect(兼容旧浏览器))
      • [方式3:HTML原生 loading="lazy"(极简,依赖浏览器支持)](#方式3:HTML原生 loading="lazy"(极简,依赖浏览器支持))
    • 四、关键优化点
      • [1. 占位图设计](#1. 占位图设计)
      • [2. 提前加载(预加载)](#2. 提前加载(预加载))
      • [3. 加载状态处理](#3. 加载状态处理)
      • [4. 性能优化](#4. 性能优化)
    • 五、兼容性与场景适配
    • 六、常见问题与解决方案
      • [1. 图片加载后布局跳动(CLS)](#1. 图片加载后布局跳动(CLS))
      • [2. 动态添加的图片不触发懒加载](#2. 动态添加的图片不触发懒加载)
      • [3. 移动端滚动卡顿](#3. 移动端滚动卡顿)
    • 七、总结

图片懒加载 前端核心笔记

一、什么是图片懒加载

图片懒加载(Lazy Load)是一种前端性能优化技术 ,核心思想是:图片不在当前视口(用户可见区域)时,不加载图片资源;当图片进入视口时,再触发加载

  • 核心价值:减少页面初始加载的资源请求数,降低首屏加载时间,节省带宽(尤其移动端/多图场景)。
  • 对比传统加载:传统方式下页面所有图片(即使在页面底部)会随页面加载一起请求,懒加载仅加载"看得见"的图片。

二、实现原理

1. 核心前提

  • 图片的src属性是触发资源请求的关键,因此懒加载的核心是先不设置真实的src,仅存储图片地址 ,待条件满足后再赋值给src
  • 监听图片是否进入视口:通过IntersectionObserver(推荐)或scroll事件+getBoundingClientRect()判断。

2. 基础实现步骤

  1. 页面渲染时,图片标签使用data-*(如data-src)存储真实图片地址,src赋值为占位图(可选,如透明图/加载中图标);
  2. 监听滚动/视口变化,判断图片是否进入用户可见区域;
  3. 满足条件时,将data-src的值赋值给src,触发图片加载;
  4. 加载完成后,移除监听(避免重复触发)。

三、具体实现方式

方式1:原生 IntersectionObserver(推荐,性能最优)

IntersectionObserver是浏览器原生API,用于监听元素与视口的交叉状态,无需频繁监听scroll事件,性能损耗低。

代码示例
html 复制代码
<!-- HTML -->
<div class="img-container">
  <!-- 占位图 + data-src存储真实地址 -->
  <img class="lazy-img" data-src="https://xxx.com/real-img1.jpg" src="placeholder.png" alt="示例图片">
  <img class="lazy-img" data-src="https://xxx.com/real-img2.jpg" src="placeholder.png" alt="示例图片">
  <img class="lazy-img" data-src="https://xxx.com/real-img3.jpg" src="placeholder.png" alt="示例图片">
</div>

<script>
// 1. 创建观察者实例
const observer = new IntersectionObserver((entries) => {
  entries.forEach(entry => {
    // 2. 判断元素是否进入视口
    if (entry.isIntersecting) {
      const img = entry.target;
      // 3. 赋值真实src,触发加载
      img.src = img.dataset.src;
      // 4. 加载完成后停止监听该元素
      observer.unobserve(img);
      // 可选:加载失败降级
      img.onerror = () => {
        img.src = "error-placeholder.png";
      };
    }
  });
}, {
  // 配置项:元素进入视口100px时提前加载(优化体验)
  rootMargin: '100px 0px'
});

// 5. 遍历所有懒加载图片,添加监听
document.querySelectorAll('.lazy-img').forEach(img => {
  observer.observe(img);
});
</script>

方式2:scroll 事件 + getBoundingClientRect(兼容旧浏览器)

通过监听window.scroll事件,结合getBoundingClientRect()获取元素位置,判断是否进入视口。

⚠️ 注意:需做防抖处理,避免频繁触发scroll导致性能问题。

代码示例
html 复制代码
<!-- HTML 同方式1 -->
<script>
// 防抖函数:减少scroll事件触发频率
function debounce(fn, delay = 100) {
  let timer = null;
  return function() {
    clearTimeout(timer);
    timer = setTimeout(() => fn.apply(this, arguments), delay);
  };
}

// 懒加载核心逻辑
function lazyLoad() {
  const imgs = document.querySelectorAll('.lazy-img');
  imgs.forEach(img => {
    // 跳过已加载的图片
    if (img.src === img.dataset.src) return;
    
    // 获取图片位置:top为元素顶部到视口顶部的距离
    const rect = img.getBoundingClientRect();
    // 判断是否进入视口(rect.top <= 视口高度)
    if (rect.top <= window.innerHeight) {
      img.src = img.dataset.src;
      img.onerror = () => {
        img.src = "error-placeholder.png";
      };
    }
  });
}

// 初始化执行一次(加载首屏可见图片)
lazyLoad();
// 监听scroll事件,防抖执行
window.addEventListener('scroll', debounce(lazyLoad));
// 监听窗口大小变化(适配响应式)
window.addEventListener('resize', debounce(lazyLoad));
</script>

方式3:HTML原生 loading="lazy"(极简,依赖浏览器支持)

现代浏览器(Chrome 77+、Firefox 75+)支持<img>标签的loading属性,无需手写JS,浏览器原生实现懒加载。

代码示例
html 复制代码
<!-- loading="lazy" 开启原生懒加载 -->
<img src="https://xxx.com/real-img.jpg" loading="lazy" alt="示例图片">
<!-- 可选值:
     lazy:懒加载;
     eager:立即加载(默认);
     auto:由浏览器决定 -->
优缺点
  • 优点:零代码、原生支持、性能优;
  • 缺点:浏览器兼容性有限(不支持IE/低版本浏览器),无自定义加载阈值(如提前加载)。

四、关键优化点

1. 占位图设计

  • 建议使用同比例的纯色占位图/骨架屏,避免图片加载后页面布局跳动(CLS布局偏移);

  • 占位图尺寸与真实图片一致,可通过CSS固定宽高:

    css 复制代码
    .lazy-img {
      width: 100%;
      height: 300px; /* 与真实图片比例一致 */
      object-fit: cover;
    }

2. 提前加载(预加载)

  • 通过IntersectionObserverrootMarginscroll判断时增加偏移量,让图片在进入视口前100~200px开始加载,提升用户体验。

3. 加载状态处理

  • 加载中:显示加载动画/骨架屏;
  • 加载失败:设置降级占位图,避免裂图;
  • 加载完成:移除占位样式。

4. 性能优化

  • 避免在scroll事件中做复杂计算,必须加防抖;
  • 图片加载完成后,移除对应的监听(如observer.unobserve(img));
  • 多图场景下,可分批加载(如每次仅处理10张未加载的图片)。

五、兼容性与场景适配

实现方式 兼容性 适用场景
IntersectionObserver IE不支持,现代浏览器支持 中高端浏览器、移动端H5
scroll + getBoundingClientRect 全浏览器支持 需兼容IE/低版本浏览器
原生 loading="lazy" Chrome77+/Firefox75+ 无需兼容旧浏览器、极简场景

降级方案

js 复制代码
// 检测IntersectionObserver是否支持,不支持则降级为scroll方式
if (!window.IntersectionObserver) {
  // 执行scroll + getBoundingClientRect逻辑
  window.addEventListener('scroll', debounce(lazyLoad));
} else {
  // 执行IntersectionObserver逻辑
}

六、常见问题与解决方案

1. 图片加载后布局跳动(CLS)

  • 原因:图片未设置固定宽高,加载后改变页面布局;

  • 解决:给图片容器设置固定宽高,或使用aspect-ratio属性(CSS3):

    css 复制代码
    .img-container {
      aspect-ratio: 16/9; /* 图片宽高比 */
      overflow: hidden;
    }

2. 动态添加的图片不触发懒加载

  • 原因:初始监听仅执行一次,动态添加的图片未被观察;
  • 解决:动态添加图片后,重新调用observer.observe(newImg)或执行懒加载检测函数。

3. 移动端滚动卡顿

  • 原因:scroll事件触发过频繁,或图片加载占用主线程;
  • 解决:使用IntersectionObserver替代scroll,或开启图片will-change: auto优化渲染。

七、总结

  1. 懒加载核心:延迟加载非视口内图片 ,优先保证首屏性能,核心是控制src的赋值时机;
  2. 实现方案优先级:原生loading="lazy" > IntersectionObserver > scroll + getBoundingClientRect
  3. 关键优化:固定图片宽高(避免CLS)、提前加载、处理加载状态、兼容降级;
  4. 适用场景:长列表图片(如电商商品页、资讯图文页)、移动端H5、多图展示页面。
相关推荐
ZWZhangYu2 小时前
【Gradio系列】使用 Gradio 快速构建对话式 AI 应用
人工智能·状态模式
青春易逝丶5 小时前
状态模式
java·算法·状态模式
前端不太难8 小时前
做了一个 AI 鸿蒙 App,我发现逻辑变了
人工智能·状态模式·harmonyos
阿珊和她的猫1 天前
微信小程序静默授权异步问题的处理方案
微信小程序·状态模式·notepad++
前端不太难2 天前
ArkUI 的页面生命周期详解
状态模式
cyforkk2 天前
前后端联调实战:解决业务异常被误判为成功的“幽灵 Bug”
bug·状态模式
Rabbit_QL3 天前
【前端UI行话】前端 UI 术语速查表
前端·ui·状态模式
前端不太难4 天前
经典游戏 Claw 的引擎是怎么被逆向出来的
游戏·状态模式
青槿吖4 天前
SpringMVC通关秘籍(下):日期转换器、拦截器与文件上传的奇幻冒险
java·开发语言·数据库·sql·mybatis·状态模式