懒加载:拯救你的网页速度,告别“图片大堵车”!

141 阅读7分钟

当你的网页加载50张高清大图时,就像早高峰的北京地铁1号线——挤不进去还出不来!

一、什么是懒加载?

懒加载是一种设计模式,通常用于提高网页或应用程序的性能。它指的是延迟加载那些当前不需要立即显示的内容,直到用户需要查看这些内容时才进行加载。这种技术可以显著减少初始页面加载时间、节省网络带宽,并提升用户体验。

懒加载的主要应用场景

  1. 图片懒加载:当页面上有很多图片时,不是一次性加载所有图片,而是仅在图片即将进入浏览器视口(即用户可见区域)时才开始加载。
  2. 视频懒加载:与图片类似,视频资源也可以通过懒加载来优化性能,特别是在包含大量视频的页面中。
  3. 模块/组件懒加载:在单页应用(SPA)如React, Vue等框架中,可以通过懒加载的方式按需加载页面的不同部分或者路由对应的组件,而不是在首次加载时就全部加载。
  4. 广告懒加载:对于嵌入了多个广告位的网页,只有当广告接近用户的可视范围时才加载相应的广告脚本和素材,这样可以避免不必要的流量消耗。

这就是一个很经典的懒加载案例,先用一个占位符图占据位置 屏幕截图 2025-06-26 154328.png

二、为什么需要“懒”加载?

想象一下这个场景:你兴冲冲点开一个电商网站,结果页面卡得像PPT,图片加载慢如蜗牛。罪魁祸首往往是那些躲在页面底部的“图片巨兽”们!

<!-- 每个图片都像一辆重型卡车 -->
<img class="image-item" src="https://巨无霸图片地址.jpg" />

传统加载的致命问题

  1. 并发请求爆炸:浏览器同时下载几十张图 → 网络带宽被挤爆 → 像10辆车并排过独木桥
  2. 流量浪费:用户只看前3屏 → 底部图片白加载 → 像点了一桌菜只吃前3道
  3. 渲染阻塞:图片太多 → 页面卡在loading状态 → 用户怒点关闭

实现懒加载的好处

  • 提升页面加载速度:减少了初始加载的数据量,使得页面能够更快地呈现给用户。
  • 节省服务器资源和带宽:只请求必要的资源,降低了服务器的压力并节约了带宽。
  • 改善用户体验:快速展示主要内容,减少用户等待时间,同时动态加载额外内容,让用户感觉更加流畅自然。

这样会严重影响用户体验,阿里京东内部还流传着这样一句话,网页多加载0.1s,少赚1000万(哈哈哈不知道真的假的)由此可见懒加载是多么重要

三、懒加载:按需加载的智慧

解决方案出奇简单——用户看到哪,我就加载哪!就像餐厅上菜:

  • 第一屏:立刻上菜(加载图片)
  • 第五屏:等顾客翻页到那再炒菜(不加载)

技术版比喻

<!-- 把真正的菜单藏起来(data-original) -->
<img 
  lazyload="true"
  src="占位符.gif" <!-- 先给顾客看菜单封面 -->
  data-original="真正的菜.jpg" <!-- 大厨的秘密配方 -->
/>

那些需要滚动才能看见的页面,我们为什么要先加载呢?这不纯纯影响我们页面的加载速度,时间就是金钱呀

所以我们先必须加载我们在外面整个视窗document.documentElement.clientHeight的图片,这样才能保证用户的体验

四、代码解剖室:懒加载如何实现?

这里是我们html结构

<img class="image-item" lazyload="true" 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" />
    <img class="image-item" lazyload="true" 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" />
    <img class="image-item" lazyload="true" src="https://static.360buyimg.com/item/main/1.0.12/css/i/loading.gif"
        data-original="https://img.36krcdn.com/hsossms/20250312/v2_1c88dc26ff9341cf8738d670896ce3a8@5284654_oswg847922oswg1440oswg600_img_png?x-oss-process=image/resize,m_mfit,w_960,h_400,limit_0/crop,w_960,h_400,g_center/format,webp" />
    <img class="image-item" lazyload="true" src="https://static.360buyimg.com/item/main/1.0.12/css/i/loading.gif"
        data-original="https://img.36krcdn.com/hsossms/20250312/v2_e1d92f43af2c4f47b8852ea8786e606f@6100851_oswg635095oswg1053oswg495_img_png?x-oss-process=image/resize,m_mfit,w_960,h_400,limit_0/crop,w_960,h_400,g_center/format,webp" />
    <img class="image-item" lazyload="true" src="https://static.360buyimg.com/item/main/1.0.12/css/i/loading.gif"
        data-original="https://img.36krcdn.com/hsossms/20250307/v2_9295b22d4a1b4b55ac4c3379b2da80cc@6100851_oswg781048oswg1053oswg495_img_png?x-oss-process=image/resize,m_mfit,w_600,h_400,limit_0/crop,w_600,h_400,g_center/format,webp" />
    <img class="image-item" lazyload="true" src="https://static.360buyimg.com/item/main/1.0.12/css/i/loading.gif"
        data-original="https://img.36krcdn.com/hsossms/20250306/v2_6ea048ac01c3408a9ed6ebe79a8fc8a2@5888275_oswg849213oswg1053oswg495_img_png?x-oss-process=image/resize,m_mfit,w_600,h_400,limit_0/crop,w_600,h_400,g_center/format,webp" />
    <img class="image-item" lazyload="true" src="https://static.360buyimg.com/item/main/1.0.12/css/i/loading.gif"
        data-original="https://img.36krcdn.com/hsossms/20250312/v2_e4c73b024bcc409fba427adb2d7fb2fa@000000_oswg1251602oswg1080oswg559_img_000?x-oss-process=image/format,jpg/interlace,1" />
    <img class="image-item" lazyload="true" src="https://static.360buyimg.com/item/main/1.0.12/css/i/loading.gif"
        data-original="https://img.36krcdn.com/hsossms/20250312/v2_9f21750bb37243128b6b1790f9072649@000000_oswg1219724oswg1080oswg601_img_000?x-oss-process=image/format,jpg/interlace,1" />
    <img class="image-item" lazyload="true" src="https://static.360buyimg.com/item/main/1.0.12/css/i/loading.gif"
        data-original="https://img.36krcdn.com/hsossms/20250312/v2_2acf9b228cd940c1b5fdb5691c0b6e4c@000000_oswg1402679oswg1080oswg527_img_000?x-oss-process=image/format,jpg/interlace,1" />
    <img class="image-item" lazyload="true" src="https://static.360buyimg.com/item/main/1.0.12/css/i/loading.gif"
        data-original="https://img.36krcdn.com/hsossms/20250312/v2_af4dd443d261445d8e903c473cac074c@000000_oswg1118331oswg1080oswg497_img_000?x-oss-process=image/format,jpg/interlace,1" />
    <img class="image-item" lazyload="true" src="https://static.360buyimg.com/item/main/1.0.12/css/i/loading.gif"
        data-original="https://img.36krcdn.com/hsossms/20250313/v2_e7de1cc8e8014122ba303ea036eea532@1743780481_oswg58583oswg1080oswg257_img_000?x-oss-process=image/format,jpg/interlace,1" />
    <img class="image-item" lazyload="true" src="https://static.360buyimg.com/item/main/1.0.12/css/i/loading.gif"
        data-original="https://img.36krcdn.com/hsossms/20250313/v2_0f70e0a75a8d4736a050e846cd6ab3e5@1743780481_oswg183216oswg1080oswg629_img_000?x-oss-process=image/format,jpg/interlace,1" />

步骤1:标记可疑图片

用自定义属性data-original存放真实URL,lazyload作为开关:

<img 
  lazyload="true" 
  src="loading.gif" 
  data-original="real-image.jpg" 
/>

占位图的小心机
使用同一张极小的loading图(可缓存)→ 几十张图只需1次请求 → 像发100张相同优惠券只印刷1次

步骤2:侦察兵系统(可视区域检测)

核心武器:getBoundingClientRect() + 滚动监听/DOMContentLoaded 以及至关重要的我们整个窗口高度document.documentElement.clientHeight

const viewHeight = document.documentElement.clientHeight; // 获取战场高度
const eles = document.querySelectorAll('img[data-original][lazyload]'); // 锁定目标

const lazyload = () => {
  eles.forEach(item => {
    const rect = item.getBoundingClientRect(); // 获取图片位置情报
    
    // 判断是否进入战场(可视区)
    if (rect.bottom >= 0 && rect.top < viewHeight) {
      // 满足条件:立即行动!
    }
  });
};

// 部署侦察兵
window.addEventListener('scroll', lazyload); // 滚动时巡逻
document.addEventListener('DOMContentLoaded', lazyload); // 首次加载搜敌 只要我们的DOM结构加载完成后就执行

什么是getBoundingClientRect() 大家可以看下面这张图,其实就是当前元素位置与整个视窗的各个距离 image.png

  • top: 元素顶部相对于视口顶部的距离。
  • right: 元素右边相对于视口左边的距离。
  • bottom: 元素底部相对于视口顶部的距离。
  • left: 元素左边相对于视口左边的距离。
  • width: 元素的宽度(包括内边距和边框,但不包括外边距)。
  • height: 元素的高度(同样包括内边距和边框,但不包括外边距)。

我们可以打印上述结果看看

image.png

步骤3:特种兵作战(异步加载图片)

为什么需要new Image()?避免网速慢时页面“卡脖子”!

if (rect.bottom >= 0 && rect.top < viewHeight) {
  (function() {
    const img = new Image(); // 创建特种兵(不阻塞页面)
    img.src = item.dataset.original; // 秘密潜入下载
    
    img.onload = function() {
      // 任务完成!替换占位图
      item.src = item.dataset.original; 
      
      // 销毁证据(避免重复加载)
      item.removeAttribute('data-original');
      item.removeAttribute('lazyload');
    }
  })();
}

关键技巧解析

  1. 异步加载new Image()在后台偷偷加载 → 像服务员后厨备菜不让你看见
  2. 加载完成再替换:确保用户看到的是完整图片 → 菜没炒好绝不端上桌
  3. 移除属性:加载后删除标记 → 特种兵任务结束就撤退(节省内存)

来看看具体效果,我们先只管当前视窗,也就是不管滚动后图片的加载,也就是document.addEventListener('DOMContentLoaded', lazyload)

B1E595BDE5B620234856_converted.gif 我们只会加载视窗的图片,如果再配合滚动事件,我们是不是就完成了我们需要的效果呢?我们来试试 window.addEventListener('scroll', lazyload); 真是看着神清气爽啊 B1E595BDE5B620235503_converted.gif

五、懒加载的哲学思考

为什么这种模式如此高效?因为它深刻把握了用户行为规律

  1. 空间局限:人眼只能聚焦屏幕区域 → 加载非可视区图片=浪费
  2. 时间序列:用户从上往下浏览 → 按顺序加载最符合直觉
  3. 资源博弈:网络带宽有限 → 优先保障眼前体验

这就好比书店摆放畅销书:

  • 门口展台放最新畅销书(首屏图片)
  • 仓库囤积冷门书籍(未加载图片)
  • 顾客询问时才去仓库取(滚动触发加载)

总结:懒加载的三大军规

  1. 藏真图data-original保存真实URL,src放占位图
  2. 听滚动scroll事件触发可视区域检测
  3. 异步换new Image()后台加载,完成再替换

最终效果对比:

image.png

记住:在Web性能优化领域, “懒”不是缺点,而是智慧!  当你下次看到页面飞速加载时,不妨感谢那些“懒洋洋”的图片——它们只是在该出现时才闪亮登场罢了!