解锁懒加载:提升性能的神奇魔法

一、什么是懒加载

懒加载是一种资源加载优化技术,核心是 “按需加载”,只在资源即将进入视野时才加载。

二、懒加载的作用

  • 减少初始加载时间,提升页面打开速度。
  • 降低服务器初始请求压力,节省带宽资源。
  • 减少设备内存占用,提升页面运行流畅度。

三、常见应用场景

  • 长列表页面(如商品列表、资讯流)的图片加载。
  • 包含大量视频的页面(仅加载当前可视区域视频)。
  • 非首屏的组件或脚本(如底部评论区、统计脚本)。

四、懒加载的实现原理

懒加载的核心实现原理可以概括为 “延迟加载 + 条件触发”—— 初始不加载非必要资源,通过监听特定事件判断资源是否 “即将被使用”,满足条件时再动态加载资源。

具体拆解为 4 个关键步骤,结合技术细节和逻辑闭环说明:

1、核心前提:资源 “占位” 与初始状态标记

懒加载的第一步是让浏览器不主动加载目标资源,同时预留资源位置避免页面布局抖动:

1.1 不直接赋值资源地址:

比如图片,不把真实地址写在 src 属性(src 会触发浏览器自动加载),而是存到自定义属性(如 data-src、data-original);视频同理,不设置 src 或 source 的 src,改用自定义属性存储真实地址。

<!-- 图片懒加载示例:初始不加载,真实地址存在 data-src -->
<img class="lazy" data-src="真实图片地址.jpg" alt="示例" width="300" height="200">
1.2 预留布局空间:

通过 width/height 属性或 CSS 固定资源尺寸(如 aspect-ratio),避免资源加载后页面重排(回流)。

2、关键步骤:监听 “触发条件” 事件

要判断资源 “是否即将被访问”,需要监听用户行为或页面状态变化,常见触发事件有 3 类:

2.1 滚动事件(最常用):

监听 window.scroll 事件(或元素的 scroll 事件,如滚动容器内的资源),实时判断资源位置。

❗ 优化点:用 throttle(节流)限制事件触发频率(如 100ms 触发一次),避免滚动时频繁计算导致性能消耗。

2.2 视口交叉检测(现代方案):

使用 Intersection Observer API(浏览器原生 API),无需手动监听滚动,直接检测 “资源元素是否进入视口(或与视口交叉)”,性能更优、代码更简洁。

2.3 其他触发场景:

点击事件(如点击 “加载更多” 时加载下方资源);

页面加载完成后的初始检测(首屏内的 “懒加载资源” 需要初始判断是否在视口,避免漏加载)。

3、核心逻辑:判断 “是否需要加载”

通过事件触发后,计算资源位置与视口的关系,核心判断条件是:资源是否进入视口,或即将进入视口(预留缓冲)

3.1 传统计算方式(手动判断)

通过 getBoundingClientRect() 获取元素的位置信息,结合视口高度 / 宽度判断:

  • 元素顶部距离视口顶部的距离(element.top)≤ 视口高度(window.innerHeight)+ 缓冲距离(如 200px,提前加载,避免用户看到空白);
  • 元素左侧距离视口左侧的距离(element.left)≤ 视口宽度(window.innerWidth)+ 缓冲距离(适配横向滚动场景)。

核心计算代码示例:

function isInViewport(element) {
  const rect = element.getBoundingClientRect();
  const viewportHeight = window.innerHeight || document.documentElement.clientHeight;
  const viewportWidth = window.innerWidth || document.documentElement.clientWidth;
  const buffer = 200; // 提前200px加载,提升体验
  // 元素顶部进入视口(含缓冲),且元素左侧在视口内
  return rect.top <= viewportHeight + buffer && rect.left <= viewportWidth + buffer;
}
3.2 现代方案(Intersection Observer)

无需手动计算,API 自动监听元素与视口的 “交叉状态”:

  • 定义一个观察者(Observer),指定交叉阈值(如元素进入视口 10% 时触发);
  • 当元素满足交叉条件时,触发回调函数,执行加载逻辑。

4、最终动作:动态加载资源并 “解锁”

当资源满足加载条件时,执行真正的加载操作,同时避免重复加载:

4.1 动态赋值真实地址:

把自定义属性(data-src)的值赋给原生属性(src),浏览器会自动发起请求加载资源;
视频需创建 source 元素并赋值 src,或直接修改视频 src。

4.2 标记 “已加载”,避免重复处理:

加载后移除 lazy 类或添加 loaded 类,后续事件触发时跳过已加载的资源。

4.3 清理监听(可选):

若资源加载后不会再 “离开视口并需要重新加载”(如长列表图片),可停止监听该元素(如 Intersection Observer 调用 unobserve),减少性能消耗。

<!-- 1. 初始占位 -->
<img class="lazy" data-src="image1.jpg" alt="示例" width="300" height="200">
<img class="lazy" data-src="image2.jpg" alt="示例" width="300" height="200">

<script>
// 2. 监听触发事件(以 Intersection Observer 为例)
const lazyImages = document.querySelectorAll('.lazy');

// 3. 定义加载逻辑
function loadImage(img) {
  if (img.dataset.src) {
    img.src = img.dataset.src; // 动态赋值,触发加载
    img.removeAttribute('data-src'); // 移除自定义属性
    img.classList.add('loaded'); // 标记已加载
  }
}

// 4. 创建观察者,判断交叉条件
const observer = new IntersectionObserver((entries) => {
  entries.forEach(entry => {
    if (entry.isIntersecting) { // 元素进入视口
      loadImage(entry.target); // 加载资源
      observer.unobserve(entry.target); // 停止监听该元素
    }
  });
}, { rootMargin: '200px' }); // 预留200px缓冲,提前加载

// 5. 对所有懒加载图片启动监听
lazyImages.forEach(img => observer.observe(img));
</script>

5、核心总结

懒加载的实现原理本质是:“先占位不加载 → 监听触发事件 → 判断是否进入视口(含缓冲) → 动态加载资源并标记”
其中,Intersection Observer 是现代浏览器推荐方案(性能优、代码简洁),传统滚动 + 手动计算适用于需要兼容旧浏览器的场景。

五、懒加载实现代码

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
  <title>Document</title>
  <style>
.bg,.bg1,.small,.big{
  overflow: hidden;
  width: 100%;
  height: 800px;
  background-size: cover; /* 保留原有样式 */
  background-repeat: no-repeat;
  background-position: center center;
}
  </style>
</head>
<body>
<div class="bg" data-bg="https://b0.bdstatic.com/ugc/7vzLq4vmPMp_X3SVZJaslg0ea528533a78abb146f8d2c8426b952d.jpg"></div>
<div class="bg1" data-bg="https://img0.baidu.com/it/u=894097787,217968889&fm=253&app=138&f=JPEG?w=500&h=889"></div>
<div class="small" data-bg="https://pic.rmb.bdstatic.com/bjh/3f11932f0ab1/250105/8cf77de872c0c4fc63b96b7a9679d3fe.jpeg"></div>
<div class="big" data-bg="https://pics4.baidu.com/feed/ae51f3deb48f8c54a4a7709a9ce510fae1fe7f6e.jpeg@f_auto?token=284dceaa76d389c9c4bc2632df15a9c1"></div>
<script>
  // 等待页面DOM加载完成
$(document).ready(function() {
  // 定义懒加载函数
  function lazyLoad() {
    // 1. 处理img标签(logo、秒杀图标等)
    $("img[data-src]").each(function() {
      const $img = $(this);
      // 判断元素是否进入可视区域
      if (isInViewport($img)) {
        $img.attr("src", $img.data("src")).removeAttr("data-src"); // 加载图片并移除自定义属性
      }
    });

    // 2. 处理背景图(大背景、商品卡片背景等)
    $("[data-bg]").each(function() {
      const $el = $(this);
      if (isInViewport($el)) {
        $el.css("background-image", `url(${ $el.data("bg") })`).removeAttr("data-bg"); // 加载背景图
      }
    });
  }

  // 辅助函数:判断元素是否在可视区域内
  function isInViewport($el) {
    const rect = $el[0].getBoundingClientRect();
    // 元素顶部进入视口底部,或元素底部进入视口顶部,即判定为可见
    return (
      rect.top <= $(window).height() + 100 && // 提前100px加载,避免滚动时空白
      rect.bottom >= 0
    );
  }

  // 初始加载一次(加载首屏可见元素)
  lazyLoad();

  // 监听滚动事件,触发懒加载
  $(window).scroll(function() {
    lazyLoad();
  });

  // 监听窗口 resize 事件(适配屏幕变化)
  $(window).resize(function() {
    lazyLoad();
  });
});
</script>
</body>
</html>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值