首页/文章/javascript

分分钟让你实现图片懒加载

2024-07-10
17746 分钟
...

图片懒加载(Lazy Loading)是一种常见的前端性能优化技术,用于改善用户体验和页面加载速度。当用户浏览网页时,并非所有图片都需要立即加载到内存中,特别是那些位于屏幕下方、尚未进入可视区域的图片。通过懒加载,我们只在需要的时候加载图片,从而减少初始页面加载时间,节省带宽资源,提升网站性能。

一、浏览器的工作机制与懒加载的必要性

浏览器解析HTML文档并构建DOM树时,会遇到<img>标签,这通常触发图片资源的立即下载。然而,在现代Web应用中,一般的网页都含多图,如电商网站商品展示,会显著增加服务器负担,拖慢页面渲染速度并消耗额外带宽。以单张1MB图片为例,在10000用户并发访问场景下,瞬时带宽需求可达10GB,对网络资源造成巨大压力。因此,网站需采用优化策略,如图片压缩、懒加载等技术,以提升用户体验和减轻服务器负载。

实现懒加载的基本步骤

  1. 修改图片源引用: 将<img>标签中的src属性替换为data-src,后者存储实际的图片URL。浏览器不会解析data-前缀的属性来下载资源,这样图片就不会在页面加载初期被加载。
  2. 监听滚动事件: 使用JavaScript监听窗口的scroll事件,检查哪些图片进入了可视区域。一旦图片进入可视区域,就将data-src的值赋给src,触发图片的实际下载和显示。
  3. 性能考虑: 为了避免频繁触发scroll事件导致的性能问题,可以通过requestAnimationFrame或节流函数(throttle function)来限制事件处理函数的执行频率,确保即使在快速滚动时也能保持良好的性能。

二、深入理解懒加载的关键点

  • 浏览器的多线程机制: 尽管JS是单线程的,但浏览器在处理资源下载时使用了多线程。这意味着图片、CSS和JS文件等资源可以同时下载,提高加载效率。但是,过多的并发下载可能受到网络限制,如TCP/IP连接的数量限制,因此合理控制并发数是必要的。
  • DOM操作与性能: 在实现懒加载时,频繁地修改DOM可能会导致重绘和重排,影响性能。因此,应当尽量减少DOM操作,例如在每次滚动后批量更新图片源,而不是逐个处理。
  • 数据属性与datasetdata-属性是HTML5引入的一个特性,用于存储页面或应用程序的私有自定义数据。通过dataset属性,可以方便地访问这些数据属性,例如element.dataset.src获取data-src的值。

三、实现方式详解

图片懒加载的实现方法多样,下面介绍三种主流的实现方式。

1. 基础懒加载

HTML5引入了loading="lazy"属性,使得懒加载的实现变得简单。只需在<img>标签中添加该属性,浏览器便会在图片接近可视区域时自动加载。

<img src="placeholder.jpg" data-src="image-to-lazy-load.jpg" loading="lazy" alt="Lazy Loaded Image">

这里的src属性放置一个占位符图片,而实际要加载的图片路径存储在data-src中。这种方案适合于不需要高度定制化的场景。

2. 使用Intersection Observer API

对于需要更精细控制的场景,Intersection Observer API提供了强大的能力。通过监听DOM元素与视口的交集变化,就可以决定何时加载图片。


<img class="lazy" data-src="image-to-lazy-load.jpg" alt="Lazy Loaded Image">


<script>
  document.addEventListener("DOMContentLoaded", function() {
    var lazyImages = document.querySelectorAll(".lazy");
    var observer = new IntersectionObserver(function(entries, observer) {
      entries.forEach(function(entry) {
        if (entry.isIntersecting) {
          var lazyImage = entry.target;
          lazyImage.src = lazyImage.dataset.src;
          observer.unobserve(lazyImage);
        }
      });
    });

    lazyImages.forEach(function(lazyImage) {
      observer.observe(lazyImage);
    });
  });
</script>

上面通过创建了一个观察器,当图片与视口发生交集时,会触发图片的真实加载。此方法适用于需要复杂逻辑判断的页面。

3 使用lodash的节流函数来限制loadImage函数的调用频率来延迟加载图片

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <link rel="stylesheet" href="./common.css">
    <!-- 引入lodash库,用于节流函数 -->
    <script src="https://cdn.bootcdn.net/ajax/libs/lodash.js/4.17.21/lodash.min.js"></script>
</head>
<body>
    <!-- 图片列表,data-src属性存储真实的图片URL -->
    <img data-price="20" data-src="https://img.36krcdn.com/20190808/v2_1565254363234_img_jpg">
    <img data-src="https://img.36krcdn.com/20190905/v2_1567641293753_img_png">
    <img data-src="https://img.36krcdn.com/20190905/v2_1567640518658_img_png">
    <!-- 更多图片... -->

    <!-- 脚本 -->
    <script>
        // 获取所有图片元素
        const imgs = document.getElementsByTagName('img');
        const num = imgs.length; // 图片总数
        let n = 0; // 已加载图片计数器

        // 页面加载完成时触发
        document.addEventListener('DOMContentLoaded', () => {
            loadImage();
        });

        // 图片加载函数
        function loadImage() {
            // 获取屏幕高度和滚动位置
            let screenHeight = document.documentElement.clientHeight;
            let scrollTop = document.documentElement.scrollTop || document.body.scrollTop;

            // 遍历所有图片,判断是否需要加载
            for (let i = 0; i < num; i++) {
                if (imgs[i].offsetTop < screenHeight + scrollTop) {
                    // 如果图片在可视区域内,则加载图片
                    imgs[i].src = imgs[i].getAttribute('data-src');
                    n = i + 1;
                    if (n === num) {
                        // 所有图片加载完成后,移除滚动监听
                        window.removeEventListener('scroll', throttleLayLoad);
                    }
                }
            }
        }

        // 使用lodash的节流函数限制loadImage的调用频率
        const throttleLayLoad = _.throttle(loadImage, 300);
        // 监听滚动事件,触发节流后的图片加载函数
        window.document.addEventListener('scroll', throttleLayLoad);
    </script>
</body>
</html> 

四、核心价值

图片懒加载的核心价值在于优化页面加载效率,主要体现在以下几个方面:

  1. 减少初始加载时间:页面不再一次性加载所有图片,而是按需加载,显著缩短了用户等待的时间。
  2. 节省带宽与服务器资源:通过仅加载可视区域的图片,大幅降低了带宽消耗和服务器的处理负担。
  3. 提升用户体验:在用户滚动页面时动态加载图片,确保了流畅的浏览体验,尤其是在移动设备上表现更为突出。

五、业务逻辑优化与前端实践的技术

  • 预加载与预渲染: 对于即将进入可视区域的图片,可以提前进行预加载,以减少等待时间。预加载通常发生在用户滚动接近图片时,这样可以无缝地展示图片,提升用户体验。
  • 使用Intersection Observer API: 这是一个现代的API,提供了更高效的方式来检测元素是否在视口内,替代了传统的滚动事件监听。它允许开发者注册回调函数,当目标元素进入或离开视口时,自动触发回调,无需手动管理事件监听器。
  • 减少事件处理器的触发次数: 使用节流(throttle)函数,避免在短时间内频繁执行同一段代码。这有助于减少不必要的计算和DOM操作,提升页面响应速度。

如果您觉得这篇文章有帮助,请点个赞吧~

分享文章

相关文章

更多文章 →
javascript2026-02-24
navigator.sendBeacon全指南
在前端开发中,埋点系统是必不可少的一环。我们经常需要在用户 关闭页面 、 刷新 或 跳转路由 时,向服务器发送最后一条统计数据(比如用户停留时长、页面跳出率)。 但这看似简单的需求,在实现时却危机四伏:请求发不出去?页面跳转卡顿?今天我们就来聊聊这个问题的终极解决方案 —— 。 一、 痛点与传统方案的挣扎 场景还原 当用户点击关闭按钮时,浏览器会触发生命周期事件( 或 )。如果我们直接使用普通的异步 AJAX ( 或 ) 发送请求,浏览...
学习
javascript2025-11-02
理解浏览器事件系统,从用户点击到事件对象的完整旅程
深入理解浏览器事件系统:从用户点击到事件对象的完整旅程 “当我点击页面按钮时,背后发生了什么?为什么回调函数能收到一个包含丰富信息的event对象?今天,让我们一起揭开浏览器事件系统的神秘面纱。” 一个令人困惑的现象 作为前端开发者,我们每天都在写这样的代码: 这段代码如此熟悉,以至于我们很少停下来思考:​ ​这个 对象到底从哪里来?它为什么能知道点击的精确坐标?为什么能识别是哪个元素被点击了?​ ​ 更神奇的是,当我们手动创建事件时:...
学习
javascript2025-10-01
实现大文件上传全流程详解
在日常开发中,大文件上传是个绕不开的坎——动辄几百 MB 甚至 GB 级的文件,直接上传不仅容易超时,还会让用户体验大打折扣。最近我用 Vue+Express 实现了一套完整的大文件上传方案,支持分片上传、断点续传、秒传和手动中。 一、先看效果:我们要实现什么? 先上核心功能清单,确保大家明确目标,知道我们要解决哪些实际问题: 大文件分片上传 :将文件切成固定大小的小片段分批上传,避免单次请求超时 秒传 :服务器已存在完整文件时,直接返...
学习
javascript2025-09-18
JavaScript 的多线程能力:Worker
如果你写过一些计算量稍大的 JavaScript 代码,比如图像处理、大量数据排序或者复杂的算法,你几乎肯定遇到过浏览器“卡死”的现象。点击页面没反应,动画也停了,就像整个世界都静止了。 这就是主线程被阻塞的典型后果。因为主线程既要负责执行 JavaScript,又要负责渲染页面、响应用户操作,一旦它被繁重的计算任务占满,就无暇顾及其他,用户体验便直线下降。 这个问题的根源,正是“主线程是单线程的”。那么,如何解决呢? 答案很简单:把这...
学习面试
javascript2025-09-15
一张 8K 海报差点把首屏拖垮
你给后台管理系统加了一个「企业风采」模块,运营同学一口气上传了 200 张 8K 宣传海报。首屏直接飙到 8.3 s,LCP 红得发紫。 老板一句「能不能像朋友圈那样滑到哪看到哪?」——于是你把懒加载重新翻出来折腾了一轮。 解决方案:三条技术路线,你全踩了一遍 1\. 最偷懒:原生 一行代码就能跑,浏览器帮你搞定。 🔍 关键决策点 2020 年后现代浏览器全覆盖,IE 全军覆没。 必须写死 ,否则 CLS 会抖成 PPT。 适用场景...
学习
javascript2025-09-10
🚀 Web Worker让你的应用丝滑
🌟 引言 在日常的前端开发中,你是否遇到过这样的困扰: 大数据处理时页面卡死 :处理几万条数据时,页面直接卡成PPT,用户点击毫无反应 复杂计算阻塞UI :图片处理、数据分析等计算密集型任务让整个应用假死 文件上传/下载卡顿 :大文件操作时,其他功能完全无法使用 实时数据处理性能差 :WebSocket接收大量数据时,页面渲染严重滞后 今天分享6个Web Worker的核心技巧,让你的应用告别卡顿,用户体验丝滑如德芙! 💡 核心技巧...
学习

评论

请登录后发表评论

去登录
加载评论中...

目录