H5页面如何实现图片懒加载?

简介: 图片懒加载,往往作为减少首页白屏时间的一个解决方案而出现。直观的来说,就是不要直接加载所有图片,而是满足一定条件后才加载,也就是”惰性加载“。实现图片懒加载的方式有很多,如果要简单点那就直接使用第三方插件:[vue-lazyload](https://www.npmjs.com/package/vue-lazyload),如果想探究一下别人的插件是怎么实现图片懒加载的,那么可以看看本文是如何实现的。

图片懒加载,往往作为减少首页白屏时间的一个解决方案而出现。直观的来说,就是不要直接加载所有图片,而是满足一定条件后才加载,也就是”惰性加载“。实现图片懒加载的方式有很多,如果要简单点那就直接使用第三方插件:vue-lazyload,如果想探究一下别人的插件是怎么实现图片懒加载的,那么可以看看本文是如何实现的。

设计思路

实现图片懒加载我们需要先明白具体的场景,一般来说,我们会在首屏先加载几张图片,其他的图片则先不加载,在页面滚动时,图片快出现在视窗中的时候才来加载图片。为什么要这么实现呢,因为对于图片很多的场景,如果一次性加载出所有的图片,可能会导致页面白屏时间比较长,特别是图片比较大的时候。

实现过程

  1. 使用data-*自定义数据属性给img标签新增一个data-src属性
  2. 全局监听滚动事件,使用节流处理回调函数
  3. 在回调函数中,判断图片是否已经出现在可视区域,如果已经出现在可视区域,则加载该图片
  4. 页面初始化的时候执行一下回调函数,保证首屏有图片显示

在这个实现过程中,涉及一些知识点,我们来快速回顾一下:

准备知识

data-*

data-*是可自定义数据属性的属性,可用在所有的HTML元素上面,嵌入自定义的数据内容。这些自定义的数据可以在HTMMLElement.dataset中被访问到,例如:

<img id="img" src="loading.gif" data-src="xxx.png" data-name="img" />
// 访问dataset
const img = document.getElementById('img')
console.log(img.dataset.src); // xxx.png
console.log(img.dataset.name); // img

我们实现图片懒加载的最终目的,就是在恰当的时候使用data-src的值替换到src,加载真实的图片。data-*定义的数据不仅可以在js中访问,也可以在CSS中访问,具体可参考:dataset

getBoundingClientRect()

Element.getBoundingClientRect()方法会返回一个DOMRect对象,其包含了当前元素的大小,以及相对于视窗的位置信息。听名字可能会有点迷糊,但是结合图来看就比价好理解了:

element-box-diagram.png

DOMRect对象中的widthheight是包含了元素的paddingborder-width,其位置信息指的是包含元素的最小矩形的每条边距离视窗原点(0,0)的位置。

throttle

由于我们会全局监听scroll滚动事件,如果每次滚动都触发回调函数的话会造成不必要的计算成本,因此我们考虑使用节流来处理滚动事件。节流的具体细节就不在此重复,我们先简单实现一个节流:

function throttle(fn, delay = 200) {
    let timer = null;
    return function() {
        if (timer) return;
        timer = setTimeout(() => {
            fn.apply(this, arguments);
            timer = null;
        }, delay);
    }
}

window.innerHeight

有几个很相似的”height“,我们就简单都梳理一下:

  • window.innerHeight:浏览器可视区域的高度;如果有水平滚动条,也会包含滚动条高度
  • window.outerHeight:获取整个浏览器的高度
  • Element.scrollHeight:元素内容的高度,包括由于溢出导致隐藏的内容高度
  • Element.clientHeight:元素内部的高度,包含内边距,但不包括水平滚动条、边框、外边距

这里我们使用innerHeight即可,因为我们是在window对象上监听scroll滚动事件。

准备工作已经完毕,接下来就直接上手代码。

完整代码

代码中都有相应的注释,在了解上面的准备知识后,代码就挺简单的了:

js部分

// 使用for循环批量创建img,html中可没有v-for可以使用
for (let index = 0; index < 10; index++) {
    let img = document.createElement("img");
    img.src = "./loading.gif";
    img.dataset.src = "./dog.jfif"; // 由于我们是通过js创建的,因此就无法直接使用data-*,如果是在html上面,需要添加此属性
    document.body.appendChild(img);
    img = null;
}
// 节流
function throttle(fn, delay = 200) {
    let timer = null;
    return function () {
        if (timer) return;
            timer = setTimeout(() => {
            fn.apply(this, arguments);
            timer = null;
        }, delay);
    };
}
// 懒加载-回调函数
function lazyLoad() {
    const imgs = document.querySelectorAll('img[data-src]');
    if (!imgs.length) return;
    imgs.forEach(img => {
    const rect = img.getBoundingClientRect();
    if (rect.top < window.innerHeight) {
        img.src = img.dataset.src;
        img.removeAttribute('data-src'); // 我们是通过img[data-src]查找所有img标签的,渲染后就删除data-src可减少forEach循环的计算成本
        }
    })
}
// 全局监听scroll滚动事件
window.addEventListener('scroll', throttle(() => {
    lazyLoad();
}, 100));
// 初始化的时候执行一下加载图片的函数
lazyLoad();

CSS部分

<style>
body {
    display: flex;
    flex-direction: column;
    align-items: center;
}
img {
    margin: 10px auto;
    width: 600px;
    height: 400px;
    object-fit: cover;
    border-radius: 4px;
    border: 1px solid #070707;
}
</style>

运行结果:

首屏展示

image-20220621155358423.png

首先我们会默认加载三张图片,查看元素节点,这三张图片的data-src都没有,而另外没有加载的图片是有data-src的。

滚动中展示

image-20220621155602711.png

滚动时会触发图片加载的回调函数,DOM树也会跟着改变

滚动结束展示

image-20220621155723789.png

所有图片都将只有src,没有data-src

总结

本文通过监听滚动事件,在图片出现在可视区域前才加载真正的图片,如果未出现则使用默认的loading图片的方式实现了图片懒加载。一般来说,loading图片都会比较小,而实际的图片会大很,因此使用loading图片来代替是可以减少图片渲染时间的。

相关文章
|
Web App开发 缓存 JavaScript
如何处理页面关闭时发送HTTP请求?
在实际项目开发中,可能会遇到这样的业务问题:如何在用户离开或关闭页面时发送HTTP请求给服务端?可能有人会觉得页面都关闭了,还需要发送什么请求,完全没必要噻。但如果真有这样的业务需求落到自己的头上,那么我们应该如何来实现呢?
3083 0
如何处理页面关闭时发送HTTP请求?
|
5月前
|
机器学习/深度学习 人工智能 自然语言处理
53_多模态LLM:图像理解的新范式
在人工智能技术快速发展的今天,单一模态的语言模型已经无法满足日益复杂的应用需求。2025年,多模态大型语言模型(MLLM)的崛起标志着AI技术进入了一个新的发展阶段,特别是在图像理解与文本生成的结合方面取得了突破性进展。本文将深入剖析多模态LLM的技术原理、架构设计、性能评估及实际应用案例,探讨视觉-语言融合技术如何重塑AI应用的边界,以及在未来发展中面临的挑战与机遇。
|
机器学习/深度学习 前端开发 JavaScript
WebAssembly:让前端性能突破极限的秘密武器
WebAssembly(简称 WASM)作为前端开发的性能加速器,能够让代码像 C++ 一样在浏览器中高速运行,突破了 JavaScript 的性能瓶颈。本文详细介绍了 WebAssembly 的概念、工作原理以及其在前端性能提升中的关键作用。通过与 JavaScript 的配合,WASM 让复杂运算如图像处理、3D 渲染、机器学习等在浏览器中流畅运行。文章还探讨了如何逐步集成 WASM,展示其在网页游戏、高计算任务中的实际应用。WebAssembly 为前端开发者提供了新的可能性,是提升网页性能、优化用户体验的关键工具。
6941 2
WebAssembly:让前端性能突破极限的秘密武器
|
JavaScript 前端开发 Docker
如何通过pm2以cluster模式多进程部署next.js(包括docker下的部署)
通过这些步骤,可以确保您的Next.js应用在多核服务器上高效运行,并且在Docker环境中实现高效的容器化管理。
1280 44
|
弹性计算 关系型数据库 Serverless
云端架构下的高效多媒体文件处理方案测评体验
传统的服务器部署模式在处理高并发、大数据量的文件转换任务时,常面临资源瓶颈和成本上升的问题。使用函数计算,利用事件驱动和异步任务的方式,将文件处理任务与核心应用解耦,同时依靠函数计算自动弹性扩展和按使用付费的优势可以快速对多媒体文件进行处理。
聊天框(番外篇)—如何实现@功能的整体删除
上一篇文章中,我们已经初步实现了聊天输入框,但其@功能是不完善的,例如无法整体删除、无法获取除用户名以外的数据(假设用户名不是唯一的)。有问题就要想办法解决,在网上百度了一圈后,倒是有一些收获。本文就着重解决@的整体删除以及获取额外数据。
1551 0
聊天框(番外篇)—如何实现@功能的整体删除
|
Android开发
Uniapp 与原生android的交互 和简单配置
Uniapp 与原生android的交互 和简单配置
2405 0
Uniapp 与原生android的交互 和简单配置
|
消息中间件 关系型数据库 MySQL
Kafka Connect :构建强大分布式数据集成方案
Kafka Connect 是 Apache Kafka 生态系统中的关键组件,专为构建可靠、高效的分布式数据集成解决方案而设计。本文将深入探讨 Kafka Connect 的核心架构、使用方法以及如何通过丰富的示例代码解决实际的数据集成挑战。
|
测试技术 Linux C++
gtest单元测试框架介绍及简单使用
gtest单元测试框架介绍及简单使用
|
JSON 网络协议 Java
使用端口映射实现Spring Boot服务端接口的公网远程调试:详细配置与步骤解析
使用端口映射实现Spring Boot服务端接口的公网远程调试:详细配置与步骤解析