JavaScript优化图片懒加载的性能技巧

 更新时间:2024年06月13日 09:26:34   作者:睡着学  
前端发展过程中有许多性能优化的操作,比如防抖、节流和图片懒加载等,在这里我们首先聊聊图片懒加载操作,我们会经常逛像淘宝和京东等购物平台,一次性全部加载会导致加载时间长、网络资源消耗大,所以本文给大家介绍了JavaScript优化图片懒加载的性能技巧

背景

前端发展过程中有许多性能优化的操作,比如防抖、节流和图片懒加载等。在这里我们首先聊聊图片懒加载操作。

在最近的618中,我们会经常逛像淘宝和京东等购物平台。那你觉得在淘宝页面中的图片资源是打开页面时就一次性全部加载完了呢,还是在你滚轮滚动到的区域才加载图片呢。

一次性全部加载会导致加载时间长、网络资源消耗大、内存占用率高和发出大量图片请求给服务器带来的巨大压力。

所以采用的是只加载当前可见区域的图片,随着用户的滚动,当其他图片进入可见区域时,再进行加载。这种方法就是图片的懒加载。这种方式可以有效地提高页面的响应速度,特别是在图片数量较多或网络条件较差的情况下,可以避免页面加载缓慢或卡顿的现象,提供更好的用户体验。

如下图所示,淘宝页面刚打开时并不是全部加载的。

大致思路

图片懒加载布局逻辑:

  • 通过将img标签的src属性值都设置为同一个图片的url,这个图片需要尽可能小。这样即可以为后续要真正加载的图片占位置,也可以让页面布局更快地呈现出来,而不是长时间等待图片加载导致空白。
  • 将每一个img标签中真正的图片url存放在一个数据属性当中。当需要加载时将该数据属性内的内容赋值给img标签的src属性。

图片懒加载交互逻辑:

  • 首先通过获取用户可视窗口高度、滚轮到最顶端的距离和每个图片到最顶端的距离。
  • 通过监听器监听滚轮滚动事件触发懒加载函数。
  • 但是一开始就出现在可视窗口的图片需要直接加载,所以需要先运行一次懒加载函数。
  • 在懒加载函数里通过判断(图片到最顶端的距离)和(用户可视窗口高度+滚轮到最顶端的距离)的大小判断是否需要加载。

编辑代码

html部分

<img src="https://misc.360buyimg.com/mtd/pc/common/img/blank.png"
     data-src="https://img.36krcdn.com/hsossms/20240612/v2_8ec7812750194dbd831babce8806c626@000000_oswg5522709oswg1792oswg1024_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 src="https://misc.360buyimg.com/mtd/pc/common/img/blank.png"
     data-src="https://img.36krcdn.com/20190808/v2_1565254363234_img_jpg">
<img src="https://misc.360buyimg.com/mtd/pc/common/img/blank.png"
     data-src="https://img.36krcdn.com/20190905/v2_1567641293753_img_png">
<img src="https://misc.360buyimg.com/mtd/pc/common/img/blank.png"
     data-src="https://img.36krcdn.com/20190905/v2_1567640518658_img_png">
<img src="https://misc.360buyimg.com/mtd/pc/common/img/blank.png"
     data-src="https://img.36krcdn.com/20190905/v2_1567642423719_img_000">
<img src="https://misc.360buyimg.com/mtd/pc/common/img/blank.png"
     data-src="https://img.36krcdn.com/20190905/v2_1567642425030_img_000">
<img src="https://misc.360buyimg.com/mtd/pc/common/img/blank.png"
     data-src="https://img.36krcdn.com/20190905/v2_1567642425101_img_000">
<img src="https://misc.360buyimg.com/mtd/pc/common/img/blank.png"
     data-src="https://img.36krcdn.com/20190905/v2_1567642425061_img_000">
<img src="https://misc.360buyimg.com/mtd/pc/common/img/blank.png"
     data-src="https://img.36krcdn.com/20190904/v2_1567591358070_img_jpg">
<img src="https://misc.360buyimg.com/mtd/pc/common/img/blank.png"
     data-src="https://img.36krcdn.com/20190905/v2_1567641974410_img_000">
<img src="https://misc.360buyimg.com/mtd/pc/common/img/blank.png"
     data-src="https://img.36krcdn.com/20190905/v2_1567641974454_img_000">

在html部分中有一个重要的步骤。你会发现每一个img标签的src属性值都是一样的,我们可以复制链接到浏览器查看该图片。

该图片是中间的小白点:

该图片的属性如下:

可以看出这个图片是非常小的。在页面打开时,加载的都是这个图片,真正要加载的图片url放置在data-src数据属性里面。最后通过JavaScript部分实现将data-src数据属性的内容赋值给img标签的src属性,然后发送HTTP请求加载真正的图片。

css部分

img {
    display: block;
    margin-bottom: 50px;
    width: 400px;
    height: 400px;
}

body {
    background-color: gray;
}

通过设置img 标签样式,让小图片给真正要放的图片占位置。

JavaScript部分

  • 变量定义:其中imgs是包含页面中所有<img>元素的集合,可以通过索引来访问具体的图像元素;num表示图片数量;n记录被加载的图片数量。
const imgs = document.getElementsByTagName('img');
const num = imgs.length;
let n = 0
  • 在全局一个设置scroll事件监听器,当事件触发后会调用lazyload慢加载函数。
window.addEventListener('scroll', lazyload)
  • 定义一个lazyload慢加载函数:
function lazyload() {
    //可视区域的高度
    let screenHeight = document.documentElement.clientHeight;
    //滚动条距离最顶部的距离,
    let scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
    //判断图片是否存在在可视区域内
    for (let i = n; i < num; i++) {
        if (imgs[i].offsetTop > scrollTop + screenHeight) {
            break;
        } else {
            //主动触发下载
            imgs[i].src = imgs[i].getAttribute('data-src');
            //记录已经加载过的图片数量
            n = i + 1;
            if (n === num) {
                //全部加载完毕后移除滚动事件
                window.removeEventListener('scroll', lazyload);
            }
        }
    }
}
  • 首先获取可视窗口的高度screenHeight、滚动条距离最顶部的距离scrollTop和具体图片距离最顶端的距离imgs[i].offsetTop。通过for循环遍历每个img元素。

    • imgs[i].offsetTop > scrollTop + screenHeight时,也就是说该图片的区域并没有被可视区域覆盖过,所以图片不需要加载,也就没有别的后续操作了。
    • 如果imgs[i].offsetTop <= scrollTop + screenHeight时,说明该图片的区域被可视区域覆盖了,图片需要进行加载。
  • 当图片需要加载时,将真正的图片url赋值给src属性。由imgs[i].src = imgs[i].getAttribute('data-src')实现。

  • 每次加载一张图片n的值就加一。当n的值等于num的值时,也就是所有图片都加载完成后就不需要scroll事件监听器了,所有通过window.removeEventListener('scroll', lazyload)清除监听器。

  • 最后因为首屏内的图片需要直接加载,而不是通过scroll事件监听器实现加载。所有需要调用一次慢加载函数。

//方法一
document.addEventListener('DOMContentLoaded', lazyload)
//方法二
window.addEventListener('load', lazyload)
  • 方法一通过DOMContentLoaded事件监听器触发慢加载函数的速度比方法二通过load事件监听器触发慢加载函数的速度快。所以推荐方法一。

节流优化

因为scroll事件监听器在频繁的滑轮滚动会频繁触发。如果直接在事件处理函数中执行大量复杂的操作,可能会导致性能问题。

所以通过使用节流限制事件触发的频率。

const imgs = document.getElementsByTagName('img');
const num = imgs.length;
//用变量记录节流返回的函数
const throttleLazyLoad = throttle(lazyload, 200);
//滚动事件触发懒加载
window.addEventListener('scroll', throttleLazyLoad)
let n = 0
//首屏加载,DOMContentLoaded事件是DOM加载完成,不包括图片(比load事件快)
document.addEventListener('DOMContentLoaded', lazyload)
//首屏加载,load事件是DOM加载完成,包括图片(比DOMContentLoaded事件慢)
window.addEventListener('load', lazyload)
//懒加载函数
function lazyload(event) {
    //可视区域的高度
    let screenHeight = document.documentElement.clientHeight;
    //滚动条距离最顶部的距离,
    let scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
    //判断图片是否存在在可视区域内
    for (let i = n; i < num; i++) {
        if (imgs[i].offsetTop > scrollTop + screenHeight) {
            break;
        } else {
            //主动触发下载
            imgs[i].src = imgs[i].getAttribute('data-src');
            //记录已经加载过的图片数量
            n = i + 1;
            if (n === num) {
                //全部加载完毕后移除滚动事件
                window.removeEventListener('scroll', throttleLazyLoad);
            }
        }
    }
}
//节流函数
function throttle(func, limit) {
    let inThrottle;
    return function () {
        const context = this;
        const args = arguments;
        if (!inThrottle) {
            func.apply(context, args);
            inThrottle = true;
            setTimeout(() => inThrottle = false, limit);
        }
    };
}

定义了一个节流函数,它接收要执行的函数 func 和时间间隔限制 limit。内部通过一个变量 inThrottle 来标记当前是否处于节流状态。当执行返回的函数时,先判断如果不在节流状态,就立即执行目标函数,并将 inThrottle 设置为 true,同时使用 setTimeout 在指定时间间隔后将 inThrottle 恢复为 false,从而实现了在规定时间间隔内只执行一次函数的节流效果,避免了频繁触发导致的性能问题。

再次优化

在日常工作时,如果让你选择手搓一个节流函数和直接使用工具库里的函数,你肯定也会和我一样偷懒,选择直接使用工具库里的现成的函数。

首先在HTML中使用以下代码引入 Lodash 库。

<script src="https://cdn.bootcdn.net/ajax/libs/lodash.js/4.17.21/lodash.min.js"></script>

然后可以删除掉你手搓的节流函数了,使用 Lodash 库里的节流函数。

const imgs = document.getElementsByTagName('img');
const num = imgs.length;
//用变量记录节流返回的函数
const throttleLazyLoad = _.throttle(lazyload, 200);//调用Lodash库里的节流函数
//滚动事件触发懒加载
window.addEventListener('scroll', throttleLazyLoad)
let n = 0
//首屏加载,DOMContentLoaded事件是DOM加载完成,不包括图片(比load事件快)
document.addEventListener('DOMContentLoaded', lazyload)
//首屏加载,load事件是DOM加载完成,包括图片(比DOMContentLoaded事件慢)
window.addEventListener('load', lazyload)
//懒加载函数
function lazyload(event) {
    //可视区域的高度
    let screenHeight = document.documentElement.clientHeight;
    //滚动条距离最顶部的距离,
    let scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
    //判断图片是否存在在可视区域内
    for (let i = n; i < num; i++) {
        if (imgs[i].offsetTop > scrollTop + screenHeight) {
            break;
        } else {
            //主动触发下载
            imgs[i].src = imgs[i].getAttribute('data-src');
            //记录已经加载过的图片数量
            n = i + 1;
            if (n === num) {
                //全部加载完毕后移除滚动事件
                window.removeEventListener('scroll', throttleLazyLoad);
            }
        }
    }
}

呈现效果

当所有图片加载完后滚动事件就不会触发慢加载函数了,并且也有节流效果。

首屏的图片立即加载,区域图片在滚动到再加载。

以上就是JavaScript优化图片懒加载的性能技巧的详细内容,更多关于JavaScript优化图片懒加载的资料请关注脚本之家其它相关文章!

相关文章

  • js函数和this用法实例分析

    js函数和this用法实例分析

    这篇文章主要介绍了js函数和this用法,结合实例形式分析了js函数和this基本功能、原理、使用方法与操作注意事项,需要的朋友可以参考下
    2020-03-03
  • JavaScript 动态创建VML的方法

    JavaScript 动态创建VML的方法

    JavaScript 动态创建VML的方法,需要的朋友可以参考下。
    2009-10-10
  • 利用原生JS自动生成文章标题树的实例

    利用原生JS自动生成文章标题树的实例

    网上关于生成文章标题树的示例很多,这篇文章介绍的是利用原生JS实现自动生成文章标题树,实现过程很简单,有需要的可以参考借鉴。
    2016-08-08
  • layui 根据后台数据动态创建下拉框并同时默认选中的实例

    layui 根据后台数据动态创建下拉框并同时默认选中的实例

    今天小编就为大家分享一篇layui 根据后台数据动态创建下拉框并同时默认选中的实例,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2019-09-09
  • js操作iframe父子窗体示例

    js操作iframe父子窗体示例

    这篇文章主要介绍了js如何操作iframe父子窗体,需要的朋友可以参考下
    2014-05-05
  • js时间查询插件使用详解

    js时间查询插件使用详解

    这篇文章主要为大家详细介绍了js时间查询插件的使用方法,结合bootstrap进行使用,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-04-04
  • 详解JavaScript闭包的优缺点和作用

    详解JavaScript闭包的优缺点和作用

    闭包是指在 JavaScript 中,内部函数可以访问其外部函数作用域中的变量,即使外部函数已经执行完毕,这种特性被称为闭包,本文将给大家介绍一下JavaScript闭包的优缺点和作用,需要的朋友可以参考下
    2023-09-09
  • javascript实现右侧弹出“分享到”窗口效果

    javascript实现右侧弹出“分享到”窗口效果

    这篇文章主要为大家介绍了javascript实现右侧弹出“分享到”窗口效果,以一个完整的js右侧弹出“分享到”窗口的实例代码进行分析,感兴趣的小伙伴们可以参考一下
    2016-02-02
  • javascript表达式和运算符详解

    javascript表达式和运算符详解

    本文主要对js表达式与js运算符进行详细介绍。具有很好的参考价值。下面跟着小编一起来看下吧
    2017-02-02
  • javascript 内存回收机制理解

    javascript 内存回收机制理解

    javascript语言是一门优秀的脚本语言.其中包含脚本语言的灵活性外还拥有许多高级语言的特性.
    2011-01-01

最新评论