建议阅读花时间阅读,加深你对前端图片性能优化的知识点学习😜
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格式+懒加载等。
本次有关前端图片加载的性能优化篇就先到这里,文章可能有很多不足或者不完善的地方。还请大佬评论区反映建议下,我会试着改一下。💦💦💦
文章有一丢丢长,码字时间也有一丢丢长。如果可以的话,厚个脸皮求个赞。🙈🙈🙈
好的,今天就到这❤️