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自定义鼠标右键的实现原理及源码

    js自定义鼠标右键的实现原理及源码

    这篇文章主要介绍了js自定义鼠标右键的实现原理及源码,需要的朋友可以参考下
    2014-06-06
  • 详解webpack 多页面/入口支持&公共组件单独打包

    详解webpack 多页面/入口支持&公共组件单独打包

    这篇文章主要介绍了详解webpack 多页面/入口支持&公共组件单独打包,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-06-06
  • JavaScript实现简单获取当前网页网址的方法

    JavaScript实现简单获取当前网页网址的方法

    这篇文章主要介绍了JavaScript实现简单获取当前网页网址的方法,通过location对象的href方法来获取网址,非常简单实用,需要的朋友可以参考下
    2015-11-11
  • 利用JS实现获取当前系统电量情况

    利用JS实现获取当前系统电量情况

    在前端浏览器中我们可以通过使用JavaScript的navigator.getBattery()方法来获取当前系统的电池情况,本文将介绍如何使用这个API以及它在实际应用中的使用,需要的可以参考下
    2023-12-12
  • ES6中Object.assign方法使用详解

    ES6中Object.assign方法使用详解

    这篇文章主要给大家介绍了关于ES6中Object.assign方法使用的相关资料,Object.assign可以用来处理数组,但是会把数组视为对象,文中通过代码介绍的非常详细,需要的朋友可以参考下
    2024-06-06
  • 利用JS自动打开页面上链接的实现代码

    利用JS自动打开页面上链接的实现代码

    今天经过测试,实现了利用JS来自动打开页面上的链接的功能,其实比较简单,就是在页面上把链接列表列出来,然后通过JQuery的相关控制,在框架页中把链接打开,具体能做什么用,大家自己想,哈哈。
    2011-09-09
  • js实现日期级联效果

    js实现日期级联效果

    本篇文章主要是对js实现日期级联效果的实例进行了介绍,需要的朋友可以过来参考下,希望对大家有所帮助
    2014-01-01
  • 深入了解JavaScript Promise

    深入了解JavaScript Promise

    这篇文章主要为大家介绍了JavaScript Promise,具有一定的参考价值,感兴趣的小伙伴们可以参考一下,希望能够给你带来帮助
    2021-12-12
  • js实现多张图片每隔一秒切换一张图片

    js实现多张图片每隔一秒切换一张图片

    这篇文章主要为大家详细介绍了js实现多张图片每隔一秒切换一张图片,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-07-07
  • 页面载入结束自动调用js函数示例

    页面载入结束自动调用js函数示例

    当页面加载完成后自动调用预先编好的js函数,在某些特殊情况下还是比较实用的,具体实现如下,感兴趣的朋友可以参考下
    2013-09-09

最新评论