✍️性能优化之图片加载篇——让页面图片渲染更丝滑👊

12,892 阅读7分钟

在这里插入图片描述

建议阅读花时间阅读,加深你对前端图片性能优化的知识点学习😜

在这里插入图片描述

1、前言

我们知道,性能优化的一个指标就是打开网页的快慢,因为网页渲染得快能够给用户更好的体验效果。但当网速不给力的时候,难免会出现资源加载的问题。特别是加载图片的时候,本文主要讨论的就是有关图片加载的一些性能优化问题。

先总览一下全文知识👇 (文后总结有具体分类哦)

2、图片加载性能优化

2.1 图片懒加载

2.1.1 为什么要使用懒加载?🧐

创建一个新的html文件,我们首先加载3张图片,来看看它的网络请求。 使用live-server打开页面,分析在web端,http协议有关的性能问题

<body>
  <div class="container">
    <div class="img"><img  src="https://img.36krcdn.com/20210122/v2_7d557e30f1254bdc8597f5eccdeb8e29_img_png?x-oss-process=image/resize,m_mfit,w_600,h_400,limit_0/crop,w_600,h_400,g_center" alt="加载中" class="pic"></div>
    <div class="img"><img  src="https://img.36krcdn.com/20210122/v2_8327e6797c2745f99e8ecc6bae1958b4_img_png" alt="加载中" class="pic"></div>
    <div class="img"><img  src="https://img.36krcdn.com/20210122/v2_6c513ab4937d456d9554deca27e9f5f0_img_000?x-oss-process=image/resize,m_mfit,w_600,h_400,limit_0/crop,w_600,h_400,g_center" alt="加载中" class="pic"></div>
</body>

在这里插入图片描述

可以看到,这里加载的三张图片进行了三次的网络请求 每个img 都会独立的打开一个http通道,这是十分好性能的,因为此时页面由3个并发的http并发数。

当我们的图片数量很多的时候,如果每次进入页面就请求所有的图片资源,给浏览器带来很大压力,同时可能等图片加载出来用户也早就走了。 因此我们要做的就是尽量减少并发数,进入页面的时候,只请求可视区域的图片资源

2.1.2 懒加载实现原理😏

图片的来源主要是src属性

当我们把src换成data-src,由于浏览器是否发送请求就是根据是否有src属性决定的。所以此时页面并不会出现像刚才一样的图片请求。

<body>
  <div class="container">
    <div class="img"><img  data-src="https://img.36krcdn.com/20210122/v2_d4fac7849276424f89522946e8ffb128_img_000?x-oss-process=image/resize,m_mfit,w_600,h_400,limit_0/crop,w_600,h_400,g_center" alt="加载中" class="pic"></div>
  </div>
</body>

在这里插入图片描述

因此我们可以利用这一点,在没进入可视区域的时候,我们先给便签赋值data-src属性,当进入可视区之后给便签赋值src属性,进入发起网络请求。

可视区概念

可视区域就是:就是你可以看到的区域。 浏览器窗口显示网页的部分(即不包括地址栏、工具栏)就是可视区 在不同浏览器获取可视区宽高都实用的JavaScript方案:


var w = document.documentElement.clientWidth ||
  		document.body.clientWidth;

var h = document.documentElement.clientHeight || 
		document.body.clientHeight;

相关api

  • addEventListener:

EventTarget.addEventListener() 方法将指定的监听器注册到 EventTarget 上,当该对象触发指定的事件时,指定的回调函数就会被执行。

  • DOMContentLoaded:

当初始的 HTML 文档被完全加载和解析完成之后,DOMContentLoaded 事件被触发,而无需等待样式表、图像和子框架的完全加载。

  • getBoundingClientRect:

Element.getBoundingClientRect() 方法返回元素的大小及其相对于视口的位置。

  • getAttribute:

getAttribute() 方法返回指定属性名的属性值。

——MDN

2.1.3 具体操作🖐️

1、我么给便签同时加上src和data-src属性,当网页加载的时候,它会加载src属性设置的图片,此时我们设置的为同一张图片,之后会进行缓存,因此只需要一次请求,。如下图 在这里插入图片描述

2、当页面进入可视区后我们就要进行真正需要展示的图片的加载。

  • 1、监听页面加载,获取视窗大小 当HTML 文档被完全加载和解析完成之后,DOMContentLoaded 事件被触发,此时将所有的图片通过document.getElementsByTagName('img')获取到,在获取浏览器视窗的高度。
 document.addEventListener('DOMContentLoaded', () => {
 	imgs = document.getElementsByTagName('img'); // 取得所有图片
    const viewHeight = window.innerHeight || 		   docuemnt.documentElement.clientHeight; // JS 浏览器嗅探,兼容浏览器
 }
  • 监听滚动事件 同时触发lazyload回调函数,再函数里面判断图片是否出现在在视窗当中,或者已经出现过。
 window.addEventListener('scroll',lazyload,false);
  • 懒加载函数:
function lazyload() {
      for (let i = 0;i < imgs.length;i++) {
        let distance = viewHeight - imgs[i].getBoundingClientRect().top;
        //当图片出现在视窗当中,或者之前已经出现了。
        //将data-src属性的值赋给src属性,从而在页面上显示真正的图片
        if (distance > 0) {
          imgs[i].src = imgs[i].getAttribute('data-src');
        }
      }
    }

代码: html:

<div class="container">
    <div class="img"><img src="./loading.svg" data-src="https://img.36krcdn.com/20210122/v2_7d557e30f1254bdc8597f5eccdeb8e29_img_png?x-oss-process=image/resize,m_mfit,w_600,h_400,limit_0/crop,w_600,h_400,g_center" alt="加载中" class="pic"></div>
    <div class="img"><img src="./loading.svg" data-src="https://img.36krcdn.com/20210122/v2_8327e6797c2745f99e8ecc6bae1958b4_img_png" alt="加载中" class="pic"></div>
    <div class="img"><img src="./loading.svg" data-src="https://img.36krcdn.com/20210122/v2_6c513ab4937d456d9554deca27e9f5f0_img_000?x-oss-process=image/resize,m_mfit,w_600,h_400,limit_0/crop,w_600,h_400,g_center" alt="加载中" class="pic"></div>
    <div class="img"><img src="./loading.svg" data-src="https://img.36krcdn.com/20210122/v2_2ef591c9986a47099c5905682fb6365f_img_png?x-oss-process=image/resize,m_mfit,w_600,h_400,limit_0/crop,w_600,h_400,g_center" alt="加载中" class="pic"></div>
    <div class="img"><img src="./loading.svg" data-src="https://img.36krcdn.com/20210122/v2_f91f15ae0804422da41dce10a1ce37ad_img_jpg?x-oss-process=image/resize,m_mfit,w_600,h_400,limit_0/crop,w_600,h_400,g_center" alt="加载中" class="pic"></div>
    <div class="img"><img src="./loading.svg" data-src="https://img.36krcdn.com/20210121/v2_73ef9df5c01c4210a01aa46b85b0fd8a_img_jpg" alt="加载中" class="pic"></div>
    <div class="img"><img src="./loading.svg" data-src="https://img.36krcdn.com/20210122/v2_2ee8b63dde624520836e412e40251df5_img_000?x-oss-process=image/resize,m_mfit,w_600,h_400,limit_0/crop,w_600,h_400,g_center" alt="加载中" class="pic"></div>
    <div class="img"><img src="./loading.svg" data-src="https://img.36krcdn.com/20210122/v2_d500f1a71d9d414c9cd631f138f5f769_img_png" alt="加载中" class="pic"></div>
    <div class="img"><img src="./loading.svg" data-src="https://img.36krcdn.com/20210122/v2_3789126b279e4889ae75988a7523c57b_img_000?x-oss-process=image/resize,m_mfit,w_600,h_400,limit_0/crop,w_600,h_400,g_center" alt="加载中" class="pic"></div>
    <div class="img"><img src="./loading.svg" data-src="https://img.36krcdn.com/20210122/v2_d4fac7849276424f89522946e8ffb128_img_000?x-oss-process=image/resize,m_mfit,w_600,h_400,limit_0/crop,w_600,h_400,g_center" alt="加载中" class="pic"></div>
  </div>
</body>

css:

<style>
  * {
    margin: 0;
    padding: 0;
  }
  .container {
    width: 1200px;
    margin: 0 auto;
  }
  .img {
    width: 500px;
    height: 500px;
  }

  </style>

js:

<script>
    let imgs;
   document.addEventListener('DOMContentLoaded', () => {
    imgs = document.getElementsByTagName('img'); //  把所有的图片抓住
    const viewHeight = window.innerHeight || docuemnt.documentElement.clientHeight; // JS 浏览器嗅探
    console.log(viewHeight)
    function lazyload() {
      for (let i = 0;i < imgs.length;i++) {
        let distance = viewHeight - imgs[i].getBoundingClientRect().top;
        if (distance > 0) {
          imgs[i].src = imgs[i].getAttribute('data-src');
        }
      }
    }
    window.addEventListener('scroll',lazyload,false);
    },false)
  </script>

看看效果

在这里插入图片描述

2.1.4 完善❣️

代码写到这里,我们似乎实现了懒加载,在页面开始加载的时候没有把全部的图片都加载完,而是先判断图片是否滚动到了视窗,从而进行加载图片的操作。

但是,我们监听的是滚动的事件,只有滚动才会触发。

在这里插入图片描述

因此在上面的代码基础上,让我们再来添加一下首屏加载:

 window.addEventListener('load',lazyload,false);

当页面加载完之后,就自动执行一次加载,就可以了。

在这里插入图片描述

但实际上依旧存在一个弊端。在我们滚动的时候事件触发的十分频繁,会造成性能问题,需要优化一下去。

来到我们的下一个话题:节流

2.2 节流

2.2.1 概念🧐

高频事件触发,但在n秒内只会执行一次,节流会减少函数的执行频率 在上面滚动事件多次触发时,我们需要利用节流方法,减少函数执行次数。

我们可以看一下当我们滚动鼠标时, lazyload()执行的情况:随便滚动一下就会触发这么多事件 在这里插入图片描述

2.2.2 实现一个简易版节流😲

在n秒内只会执行一次

  • 当我们监听滚动事件的时候,调用的是throttle函数,接收两个参数分别是 最开始的lazyload函数,变成了一个回调函数,和一个100ms时间
window.addEventListener('scroll',throttle(lazyload,1000),false);
  • 在throttle函数里面,设置,在interval时间内执行一次fn,节约了执行次数
const throttle = (fn,interval) => {
    let last = 0;//上一次触发回调的时间
    return function () {
      let now = +new Date();
      if(now - last >= interval){
        last = now
        fn();
      }
    }
  }

就算滑到底部,也只是触发了9次lazyload函数

在这里插入图片描述 更完善的节流可以看这哦

2.3 雪碧图

雪碧图被运用在使用了很多小图标的网站上。相对于把每张小图标以.png格式文件的形式引用到页面上,使用雪碧图只需要引用一张图片。

下面这张图是截取京东官网使用的一张雪碧图:

在这里插入图片描述

这样的话我们就可以将很多张图片请求合并到一张图片上,有效减少http请求。再加上css中图片属性background-position将需要的图片展示出来。 这里介绍一下这个background-position属性: background-position:设置背景图像的起始位置

在这里插入图片描述

取得右下角的图标:


{ 
	background-image:url('https://iknow-  pic.cdn.bcebos.com/faedab64034f78f0cbe417637b310a55b2191cb6?x-bce- process=image/resize,m_lfit,w_600,h_800,limit_1');
	background-repeat:no-repeat;
	background-attachment:fixed;
	background-position:  -340px -255px; 
}

在这里插入图片描述

2.4 图片Base64编码

Base64 是一种编码格式 比起懒加载的延迟加载,这种方法直接都不需要请求。

不过它是有局限性:针对尺寸很小的图标,并且又不能与其他图片合成雪碧图的,以Base64编码的形式使用,是一个不错的选择,毕竟它可以减少http请求开销。

2.5 webp图片格式

在这里插入图片描述

可以看到图片的大小明显减少了很多。

这种格式的图片:适用大图,特别是头图,banner图,商品详情页的图片可以使用webp格式,在大小上可以减少25%,同时在清晰度上不怎么受损,这样的话就可以结合我们前面的lazyload懒加载,在http请求数量和图片大小上都实现了一定的优化。

2.5.1 webp-converter😁

使用工具进行图片性能优化

  • 首先 使用npm i webp-converter安装
  • 在网上找了一张.jpg的图片,图片大小`16.7 KB (17,194 字节)

`在这里插入图片描述

  • 创建index.js
const webp = require('webp-converter');
webp.cwebp("1.jpg","output.webp","-q 80%",function(status,error) {
  console.log(status);
})

引入webp-converter,将命名为1的jpg格式图片,优化成80%的webp图片,并且把结果命名为output.webp,很明显,大小缩小了很多。

在这里插入图片描述

总结

在优化图片加载的方法上,前面介绍了可以通过减少http请求次数,和减少图片的大小来进行优化。

  • 减少http请求数: 1、 lazyload 延迟加载 (throttle 优化懒加载过程的节流)

    2、图片Base64编码

    3、 雪碧图

  • 减少图片大小

    1、 使用webp格式

然后当它们结合一起使用更加可以提高性能,比如webp格式+懒加载等。

本次有关前端图片加载的性能优化篇就先到这里,文章可能有很多不足或者不完善的地方。还请大佬评论区反映建议下,我会试着改一下。💦💦💦

在这里插入图片描述

文章有一丢丢长,码字时间也有一丢丢长。如果可以的话,厚个脸皮求个赞。🙈🙈🙈

在这里插入图片描述

好的,今天就到这❤️

在这里插入图片描述