ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

超详细的图片预加载和懒加载教程。分析应用场景。大量插图。完整的代码。累死我了。

2021-09-04 17:02:32  阅读:170  来源: 互联网

标签:count 教程 img 显示 累死 scrollTop 加载 图片


最近接手一个项目 。 结果光安装依赖都出现了一堆 麻烦 。
好不容易处理完一个 , 又来一个 。头疼啊
看到之前有一些预加载的学习笔记。于是又查查找找 ,想想写写 把预加载和懒加载的笔记写完整
发现制图挺麻烦的!不知道你们有没有什么推荐?
写了挺久的这篇文章,有什么不对的地方欢迎评论或私聊指出

图片的预加载和懒加载

预加载和懒加载的字眼总会看到 。 其实预加载和懒加载不仅仅是用于加载图片资源。其他资源,文字,视频。都可以。

我们较常用或较需要使用的场景就是加载图片资源。

这里,我们也只讨论加载图片资源。

图片预加载

什么是图片的预加载?

提前加载所需要的图片资源,加载完毕后会缓存到本地,当需要时可以立马显示出来,以达到在预览的过程中无需等待直接预览的良好体验。

简而言之 : 就是需要显示前先加载

什么场景可能需要使用预加载

  • 漫画

我们知道漫画是一张一张连续的图片组成 。我们在看漫画时,也是一张一张看的。

但如果当我们看完一张,切换到下一张时再加载图片,那么就会有一段空白的加载时间。而且漫画图片一般比较大,如果网络不是很好,那么加载时间就会比较久。体验就会下降。

在这里插入图片描述

正如图片所示,如果我们看完 2 了 ,想看 33 却还没加载完就会大大降低体验感。

而如果能在我们预览 2 这段时间里就提前加载好 3 , 等到我们看 3 时就直接里面显示。那么就体验会好很多。

  • 图片画廊

希望用户预览图片时能够顺畅 ,而不是看到下一页的图片了,上一页的还没加载出来。

与漫画类似,但加载的图片资源会比较多。

  • 或者其他加载会比较耗时但不希望让用户看到加载时空白的场景

如要一次性显示好几张图片 ,如果让用户在看的时候加载,那么图片先后显示的过程,和出现空白的现象会让用户看见。

特别是网络不好时,用户看见空白不知道是根本没有图片,还是加载不出来。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MnO4gfyt-1630744528325)(C:\Users\Administrator\Desktop\桌面\前端\预加载大量图片.png)]

如果我们使用预加载,先显示一个进度条,告诉用户加载了多少了,当加载了百分之百后再一起全部显示出来。

那么用户起码不会看到空白,也知道是在加载中,并且根据进度条能大概判断一下加载速度。那么体验就会上升。

预加载的缺点

  • 预加载会占用较多的后台资源,因为可能一次性加载较多的图片

  • 预加载需要比较长的时间 ,一般是利用用户进行其他操作时进行。(如漫画是在用户看上一个图片时进行预加载 ) 。 或者是在等待的这段时间显示其他。(如显示进度条。

图片预加载的方法

这里介绍两个常用且实用的方法 。每个方法都有优缺点 。 也有自己使用的场景。我们应该根据实际选择合适的方法

1. 通过 CSS

步骤
  • 创建用来预加载的标签
  • 给标签使用背景图,背景图的路径是需要预加载的图片资源。并且将图片移到看不见的地方,或缩小到看不见。不能用 display=none , 否则预加载会失效
  • 当使用到已经预加载好的图片时,会直接使用缓存好的图片资源,而不需要向服务器发送请求。

demo

<!--HTML文件  -->
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>CSS 预加载</title>
    
    <!-- 引入预加载样式文件 -->
    <link rel="stylesheet" href="./preLoad.css">
    
</head>
<body>
    <!-- 点击时显示图片  -->
    <button οnclick="show()">显示</button>
    
    <ul>
        <!-- 用来预加载的标签  -->
        <li id="preImg1"/>
        <li  id="preImg2"/>
        <li sid="preImg3"/>
    </ul>
</body>
<script>
    var show = function () {
        // 点击 ul 里的标签 ,显示图片
        document.querySelector("ul").innerHTML = `
        <li><img src="https://img1.baidu.com/it/u=3263944338,3726722345&fm=26&fmt=auto&gp=0.jpg"></li>
        <li><img src="https://img2.baidu.com/it/u=2480106139,2144834787&fm=253&fmt=auto&app=120&f=JPEG?w=500&h=552"></li>
        <li><img src="https://img1.baidu.com/it/u=1904128529,1389594536&fm=26&fmt=auto&gp=0.jpg"></li>`
    }
</script>
</html>
/* 预加载样式文件 */
img {
    width: 200px;
}
/* 预加载文件 */
#preImg1 {
    background-image: url('https://img1.baidu.com/it/u=3263944338,3726722345&fm=26&fmt=auto&gp=0.jpg');
    width: 0;
    height: 0;  /* 隐藏用来预加载的标签 */
}

#preImg2 {
    background-image: url('https://img2.baidu.com/it/u=2480106139,2144834787&fm=253&fmt=auto&app=120&f=JPEG?w=500&h=552');
    width: 0;
    height: 0;
}

#preImg3 {
    background-image: url('https://img1.baidu.com/it/u=1904128529,1389594536&fm=26&fmt=auto&gp=0.jpg');
    width: 0;
    height: 0;
}
分析 :

在游览器第一次打开这个文档时,加载 CSS 过程中遇到需要加载的背景图片资源,于是会发送请求到服务器,服务器返回图片资源,游览器本地缓存。

在游览器的开发者根据(F12) 的 NewWork 项我们可以看到请求码是 200 。是服务器返回的新的响应。

请添加图片描述

此时我们先清空游览器请求资源记录

在这里插入图片描述

然后点击显示按钮,将填写了 src 的图片标签放到文档中。显示图片

在这里插入图片描述

图片里面显示,而 newWork 栏里也没有向服务器发送新的请求,因为服务器就有缓存。即使有 状态码也是 304 (表示本地资源)

在这里插入图片描述

2. 通过 JavaScript

步骤
  • 将需要预加载的图片资源的 URL 保存在数组里
  • 循环遍历 URL 数组执行以下步骤,直到结束
  • 创建一个 image 对象 new Image()
  • 将 image 对象的 src 属性的值指定为预加载图片的 URL
demo
<!-- HTML 文件 -->
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>JS 预加载</title>
    <!-- 简单的样式,不用关注 -->
    <style>
        ul {
            float: left;
            margin: 0;
            padding: 0;
        }

        li {
            list-style-type: none;
            float: left;
        }

        img {
            width: 100px;
            height: 100px;
        }

        #mask {
            position: absolute;
            width: 300px;
            height: 100px;
            background-color: rgb(177, 177, 177);
            line-height: 100px;
            text-align: center;
            color: rgb(255, 255, 255);
            font-size: 32px;
            font-weight: 600;
        }
    </style>
</head>

<body>
     <!-- 设置遮罩层,显示加载进度 -->
    <div id="mask">00%</div>
    <ul>
        <!-- 写好标签,会解析到此时会立马加载 -->
        <li><img src="https://img1.baidu.com/it/u=3263944338,3726722345&fm=26&fmt=auto&gp=0.jpg"></li>
        <li><img src="https://img2.baidu.com/it/u=2480106139,2144834787&fm=253&fmt=auto&app=120&f=JPEG?w=500&h=552">
        </li>
        <li><img src="https://img1.baidu.com/it/u=1904128529,1389594536&fm=26&fmt=auto&gp=0.jpg"></li>
    </ul>
</body>
     <!-- 引入预加载脚本文件 -->
<script src="./preLoad.js"></script>
</html>
// 预加载脚本文件
// 预加载的图片 URL 
const urlList = [
    'https://img1.baidu.com/it/u=3263944338,3726722345&fm=26&fmt=auto&gp=0.jpg',
    'https://img2.baidu.com/it/u=2480106139,2144834787&fm=253&fmt=auto&app=120&f=JPEG?w=500&h=552',
    'https://img1.baidu.com/it/u=1904128529,1389594536&fm=26&fmt=auto&gp=0.jpg']

// 预加载函数 ,会在加载完毕后隐藏遮罩层
function preLoad() {
    let process = document.querySelector('#mask'),  // 遮罩层 ,用于修改进度和隐藏
        len = urlList.length,// 提出 URL 数组长度 ,提供性能
        count = 0,  // 计算已加载数量和修改进度
        ul = document.querySelector("ul"), // 将图片放入此
        imgList="";  // img 标签字符串临时存放点,避免刷新DOM的次数。
 
     // 为了模拟多图片资源和将加载过程慢下来,这里使用了计时器 , 实际情况我们采用遍历 URL 数组。
    let id = setInterval(() => {
        let img = new Image()
        img.src = urlList[count]
        imgList += `<li><img src="${urlList[count]}"></li>`
        img.onload = img.onerror = function () {
            count++;
            process.innerText = (count / len * 100).toFixed(2) + '%'; // 设置进度百分比
            if (count === len) {
                clearInterval(id)
                ul.innerHTML += imgList;
                process.style.display = 'none'
            }
        }
    }, 500)
}

// 调用预加载函数
preLoad()
分析

当需要显示大量图片时,我们先显示其他或者有一些加载的提示。同时执行预加载函数。这里我们使用了遮罩层和进度显示

预加载函数会加载图片资源并本地缓存,不会显示。但有缓存。在加载的过程中,随着加载完成的数量增加,进度增加。

当全部加载完毕后,将图片动态添加到页面上,隐藏遮罩层。图片可被立即显示,无需再发送请求到服务器请求资源。
请添加图片描述
请添加图片描述

预加载分类

无序预加载

如预览大量图片时,或表情包,它关心的是什么时候全部加载完,然后一起显示出来,其顺序不重要

表情包的预加载
const srcList = ['src1','src2','src3',...]
function preLoad() {
    let count = '';
        for (let i = 0; i < emojiSrcs.length; i++) {
            var img = new Image()	
            img.src = srcList[i]  
            
            img.onload = img.onerror = function () {	// 无论加载成功还是失败都会执行
                count++
                if (count == emojiSrcs.length - 1) {		// 如果全部加载完毕
                    mask.style.display = "none";		// 隐藏遮挡层
                    emoji.style.display = "block";		// 显示emoji
                }
            }
        }
    }
 preLoad()

有序预加载

如漫画,它不仅仅关系什么时候全部加载完,还有需要有一定的顺序。

    // 使用全局变量,保存计数
    var count = 0
    const emojiSrcs = ['src1','src2','src3',...]
    function preLoad() {
        var img = new Image()
        img.src = emojiSrcs[count]
        count++
        // 加载成功和失败都会执行
        img.onload = img.onerror = function () {
   		   if(count === emojiSrcs.length)
          	  return
           preLoad()
        }
    }
	preLoad()
	// 当第一张加载后会加载第二张,第二种加载后再加载第三张,于是可以在我们预览第一张时就可以预先加载第二张,第三张
	// 相比无序加载,不需要等使用加载完毕才能预览,而且不会预览到第二张时,第二张没加载好,但是第三张或更后的图片先加载了。

图片的懒加载

什么是懒加载

图片的懒加载是等图片在用户要看到时才进行加载 。

与预加载相反 。 预加载可以说是勤劳,先做完然后万事大吉 。 而懒加载却是需要我再做 , 不要我就准备着。

简而言之 懒加载就是 先显示再加载

什么场景需要懒加载

  • 电商

我们知道大量商品展示是分页请求的 。也就是我们会看到拉到底了再重新加载。但屏幕看到的仅是一页请求内容的部分内容。

电商搜索产品时图片会有很多,但单个图片资源又不大。因此适合应用懒加载。避免不必要的图片的加载和请求。

如 :当我们在某宝,某东,某夕夕买东西时,假设搜索完后看到一个进度条,告诉我们还在加载。或者是显示一堆空白,然后先加载出来的先显示(但可能需要往下翻一翻) 。可是 ,我们可能选择的是第一家店 。 那么其他的就并不需要加载了。

  • 其他图片较多场景

这与画廊在线图库这里场景不一样 。 图片多可能主要显示的不仅仅是图片 。 如显示大量文章,文章有一些配图 。

懒加载的缺点

  • 需要监听图片是否显示,比较耗游览器性能。
  • 图片是显示时才去加载。如果网络不太好可能会有一段时间是空白

图片懒加载的方法

原理

利用自定义属性将图片的 URL 存放到图片标签身上 , 图片的 src 为空或者用其他较小的图片资源代替(提示加载中)

图片的懒加载主要是监听 body 或者其他存放图片且滚动的元素的 scroll 事件.

在每一次事件触发时,通过相关的 API 和属性,检查图片是否显示 。

如果显示就将 URL 填到 src 属性中 ,加载图片 。如果没有显示则不进行操作。
请添加图片描述

如何检测图片是否显示

对于理解懒加载的原理其实不难 。 懒加载的关键点也是重难点就在于如何检测图片是否处理可视区内。

下面借助两张图来理解和学习。

请添加图片描述

  • clientHeight : 可视区高度

可视区高度可使用 window.innerWidth 获得 。但这个属性 IE9 以下并不支持。 因此兼容性写法为:

let clientWidth=window.innerWidth|| document.documentElement.clientWidth|| document.body.clientWidth;

关于此兼容

  • offsetTop

子图片相对父元素的偏移量。let offsetTop = imgNode.offsetTop

  • scrollTop

滚动元素顶部被滚出显示区的高度。 let scrollTop = scrollNode.scrollTop

**注意 ,这个元素必须是有滚动条才可以,否者是 0 **

如果滚动区域是整个页面,也就是仅 HTML 出现了滚动条 ,其他不设置滚动条 。

那么通过 document.documentElement.scrollTop 来获取 scrollTop 的值

一般,我们只检查整个页面被隐藏部分

如上图所示 : 当 scrollTop + clientHeight > offsetTop 时 , 图片便是显示出来或者在可视区之上了。

请添加图片描述

  • offsetHeight : 图片节点的宽高

可以通过设置好的高度自己设置。写死。

或者

element.clientHeight  // 内容加内边距
element.offsetHeight  // 内容加内边距加边框

如上图所示 :当 offsetTop + offsetHeight > scrollTop 时 , 图片就在可视区内或可视区下。

结合两个图

if ( v.offsetTop < clientHeight + scrollTop &&  v.offsetTop + v.offsetHeight > scrollTop) {
  // 图片在显示区内
}

demo

// 懒加载
// 参数 : 需要进行预加载的图片的 id (#id) , 类名(.class) ,或标签名(img)
export default function lazyLoad(imgSelector) {

  const clientHeight = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth, // 兼容获取
    scrollTop = document.documentElement.scrollTop;  //  获取页面被卷出去的顶部的高度大小

  let imgsNodes = document.querySelectorAll(imgSelector);  // 获取预加载图片列表


  // 遍历检查图片是否在可视区内
  imgsNodes.forEach((v) => {
    if (
      v.offsetTop < clientHeight + scrollTop &&
      v.offsetTop + v.offsetHeight > scrollTop
    ) {
      // 设置正确的 src 

      // 实际使用时
      // v.src = v.getAttribute("srcString");

      //  这里使用计时器模拟加载时间 ,为能看到是先显示再加载的效果 。
      setTimeout(() => {
        v.src = v.getAttribute("data-srcString");
      }, 800);
    }
  });
}

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>懒加载</title>
</head>
<style>
    img {
        width: 300px;
        height: 300px;
        display: block;
        margin: 0 auto;
        border: 1px solid #ccc;
    }
</style>

<body>
    <div>
        <img data-srcString="https://img1.baidu.com/it/u=3263944338,3726722345&fm=26&fmt=auto&gp=0.jpg"
            src="https://z3.ax1x.com/2021/09/04/hguVeK.png">
        <img data-srcString="https://img1.baidu.com/it/u=3263944338,3726722345&fm=26&fmt=auto&gp=0.jpg"
            src="https://z3.ax1x.com/2021/09/04/hguVeK.png">
        <img data-srcString="https://img1.baidu.com/it/u=3263944338,3726722345&fm=26&fmt=auto&gp=0.jpg"
            src="https://z3.ax1x.com/2021/09/04/hguVeK.png">
        <img data-srcString="https://img1.baidu.com/it/u=3263944338,3726722345&fm=26&fmt=auto&gp=0.jpg"
            src="https://z3.ax1x.com/2021/09/04/hguVeK.png">
        <img data-srcString="https://img1.baidu.com/it/u=3263944338,3726722345&fm=26&fmt=auto&gp=0.jpg"
            src="https://z3.ax1x.com/2021/09/04/hguVeK.png">
        <img data-srcString="https://img1.baidu.com/it/u=3263944338,3726722345&fm=26&fmt=auto&gp=0.jpg"
            src="https://z3.ax1x.com/2021/09/04/hguVeK.png">
        <img data-srcString="https://img1.baidu.com/it/u=3263944338,3726722345&fm=26&fmt=auto&gp=0.jpg"
            src="https://z3.ax1x.com/2021/09/04/hguVeK.png">
        <img data-srcString="https://img1.baidu.com/it/u=3263944338,3726722345&fm=26&fmt=auto&gp=0.jpg"
            src="https://z3.ax1x.com/2021/09/04/hguVeK.png">
        <img data-srcString="https://img1.baidu.com/it/u=3263944338,3726722345&fm=26&fmt=auto&gp=0.jpg"
            src="https://z3.ax1x.com/2021/09/04/hguVeK.png">
    </div>
</body>

<script type="module">
    // 导入模块
    import lazyLoad from './lazyLoad.js'
    // 使用 onl oad , 以免文档没有加载完就进行操作
    window.onload  = function(){
        // 先调用一次,将可能在显示区的照片先加载
        lazyLoad('img')
        // 监听滚动事件
        window.addEventListener('scroll',lazyLoad.bind(this,'img'))  // 利用 bind() 传参
    }
</script>
</html>

懒加载优化

前面我们提高懒加载的缺点 , 它对服务器友好,对游览器却不友好。

因为一直监听滚动事件,一直触发是非常消耗性能的 。

对此我们可以进行防抖和节流来减少性能的消耗 。提高流畅度。

节流

图片是有一定的宽高的,并不是稍微移动一点点就进行获取图片节点呀 然后遍历呀 。

其他不变,只修改 HTML 的 script 标签内的代码

<script type="module">
    import lazyLoad from './lazyLoad.js'

    window.onload  = function(){
        lazyLoad('img')  // 先执行

        let stop = true;  // 节流阀
        
        function throttle (){  // 节流函数
         if(stop){   // 如果被节流了
            setTimeout(() => {
                stop = false
                console.log('节流');
            }, 100);
            return 
         }
         stop=true
         lazyLoad('img');
         return;
        }

        window.addEventListener('scroll',throttle)  //  scroll 的执行函数改为节流函数
    }
</script>

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ONol7f3G-1630744528372)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210904160421631.png)]

当滑动滚动条时 , 可以看到 节流时输出节流 和 执行时输出执行 是有交替的 。 达到了一定的节流效果。

防抖

为什么要防抖,因为可能有以下这个场景 。我们知道 我们要的图片得往下滑好几页 。 所以前面的几页图片是可以不需要显示的。

或者前面的之前我们已经预览过了,我希望预览比较靠后的图片。那么前面的也可以不用去加载了。

那么这种情况就可以使用防抖技术了。

其他不变 ,在原来的节流函数进行小修改

    let stop = true;
    let timeID = '';   // 增加一个变量保存倒计时器的 ID

    function throttle (){
     if(stop){
        setTimeout(() => {
            stop = false
        }, 100);

        return;
     }
     stop=true

     // 删除上一个倒计时器  
    clearTimeout(timeID)
	// 重新设置倒计时器
    timeID = setTimeout(()=>{
        lazyLoad('img');
        return 
    },100)  // 延缓 0.1 秒后加载
    }   

但防抖大部分情况是不需要的,除非在防抖这段时间内的空白无太大影响

参考文章

js实现图片懒加载原理

js原生实现高性能懒加载(分步解析)

标签:count,教程,img,显示,累死,scrollTop,加载,图片
来源: https://blog.csdn.net/NiHoAjy/article/details/120102022

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有