前端图片优化

图片类型和使用场景

常用的web图片格式有:JPEG/JPGPNGWebPBase64SVG

JPEG/JPG
特点:有损压缩、体积小、加载快、不支持透明
适用场景:适用于呈现色彩丰富的图片,在我们日常开发中,JPG 图片经常作为大的背景图、轮播图或 Banner 图出现。

PNG
特点:无损压缩、质量高、体积大、支持透明
适用场景:PNG 在处理线条和颜色对比度方面有优势,主要用它来呈现小的 Logo、颜色简单且对比强烈的图片或背景等。

SVG
特点:文本文件、体积小、不失真、兼容性好
适用场景:将 SVG 写入独立文件后引入 HTML

1
<img src="文件名.svg" alt="">

Base64
特点:文本文件、依赖编码、小图标解决方案
适用场景:小图标,更新频率非常低,作为雪碧图的补充

WebP

WebP 是 Google 专为 Web 开发的一种旨在加快图片加载速度的图片格式,它支持有损压缩和无损压缩。

特点:全能但存在兼容性问题
使用场景:限制我们使用 WebP 的最大问题不是“这个图片是否适合用 WebP 呈现”,而是“浏览器是否允许 WebP

图片优化方案

减少图片体积

  • 采用WebP格式的图片,能够在不降低图片质量的前提下减小图片的体积。

WebP 的优势是它具有更优的图像数据压缩算法,能带来更小的图片体积,而且拥有肉眼识别无差异的图像质量;同时具备了无损和有损的压缩模式、Alpha 透明以及动画的特性,在 JPEG 和 PNG 上的转化效果都相当优秀、稳定和统一。

如何判断当前浏览器是否支持WebP

参考谷歌网站做法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// check_webp_feature:
// 'feature' can be one of 'lossy', 'lossless', 'alpha' or 'animation'.
// 'callback(feature, result)' will be passed back the detection result (in an asynchronous way!)
function check_webp_feature(feature, callback) {
var kTestImages = {
lossy: "UklGRiIAAABXRUJQVlA4IBYAAAAwAQCdASoBAAEADsD+JaQAA3AAAAAA",
lossless: "UklGRhoAAABXRUJQVlA4TA0AAAAvAAAAEAcQERGIiP4HAA==",
alpha: "UklGRkoAAABXRUJQVlA4WAoAAAAQAAAAAAAAAAAAQUxQSAwAAAARBxAR/Q9ERP8DAABWUDggGAAAABQBAJ0BKgEAAQAAAP4AAA3AAP7mtQAAAA==",
animation: "UklGRlIAAABXRUJQVlA4WAoAAAASAAAAAAAAAAAAQU5JTQYAAAD/////AABBTk1GJgAAAAAAAAAAAAAAAAAAAGQAAABWUDhMDQAAAC8AAAAQBxAREYiI/gcA"
};
var img = new Image();
img.onload = function () {
var result = (img.width > 0) && (img.height > 0);
callback(feature, result);
};
img.onerror = function () {
callback(feature, false);
};
img.src = "data:image/webp;base64," + kTestImages[feature];
}

原理:对每一种格式的webp(有损,无损,alpha通道,动态)生成一个很小像素的图片,在浏览器中渲染,如果没有问题,就代表支持webp。

也可以让服务器端来判断返回什么格式的图片,服务器根据 HTTP 请求头部的 Accept 字段来决定返回什么格式的图片。当 Accept 字段包含 image/webp 时,就返回 WebP 格式的图片,否则返回原图。

  • SVGO

利用SVGO压缩svg文件的大小

缓存

采用HTTP缓存

谷歌最佳实践中指出:

对于静态素材资源或不常变化的素材资源,我们建议至少缓存 1 周,至多缓存 1 年。

图片懒加载(LazyLoad)

原理

将页面中的img标签src指向一张小图片或者src为空,然后定义data-src属性指向真实的图片。src指向一张默认的图片,否则当src为空时也会向服务器发送一次请求。可以指向loading的地址。

注:图片要指定宽高,才能正确获取img.offsetTop

具体实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<head>
<meta charset="UTF-8">
<title>Document</title>
<style>
img {
display: block;
margin-bottom: 50px;
width: 400px;
height: 400px;
}
</style>
</head>

<body>

<img src="default.jpg" data-src="http://ww4.sinaimg.cn/large/006y8mN6gw1fa5obmqrmvj305k05k3yh.jpg" alt="">
<img src="default.jpg" data-src="http://ww4.sinaimg.cn/large/006y8mN6gw1fa5obmqrmvj305k05k3yh.jpg" alt="">
<img src="default.jpg" data-src="http://ww1.sinaimg.cn/large/006y8mN6gw1fa7kaed2hpj30sg0l9q54.jpg" alt="">
<img src="default.jpg" data-src="http://ww1.sinaimg.cn/large/006y8mN6gw1fa7kaed2hpj30sg0l9q54.jpg" alt="">
<img src="default.jpg" data-src="http://ww4.sinaimg.cn/large/006y8mN6gw1fa5obmqrmvj305k05k3yh.jpg" alt="">
<img src="default.jpg" data-src="http://ww4.sinaimg.cn/large/006y8mN6gw1fa5obmqrmvj305k05k3yh.jpg" alt="">
<img src="default.jpg" data-src="http://ww4.sinaimg.cn/large/006y8mN6gw1fa5obmqrmvj305k05k3yh.jpg" alt="">
<img src="default.jpg" data-src="http://ww4.sinaimg.cn/large/006y8mN6gw1fa5obmqrmvj305k05k3yh.jpg" alt="">
<img src="default.jpg" data-src="http://ww1.sinaimg.cn/large/006y8mN6gw1fa7kaed2hpj30sg0l9q54.jpg" alt="">
<img src="default.jpg" data-src="http://ww4.sinaimg.cn/large/006y8mN6gw1fa5obmqrmvj305k05k3yh.jpg" alt="">
<img src="default.jpg" data-src="http://ww4.sinaimg.cn/large/006y8mN6gw1fa5obmqrmvj305k05k3yh.jpg" alt="">

</body>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<script>
var num = document.getElementsByTagName('img').length;
var img = document.getElementsByTagName("img");
var n = 0; // 存储图片加载到的位置,避免每次都从第一张图片开始遍历

lazyload(); // 页面载入完毕加载可视区域内的图片

window.onscroll = lazyload;

function lazyload() {
var seeHeight = document.documentElement.clientHeight;
var scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
for (var i = n; i < num; i++) {
// img从页面底部开始进入可视区域
if (img[i].offsetTop < seeHeight + scrollTop) {
if (img[i].getAttribute("src") == "default.jpg") {
img[i].src = img[i].getAttribute("data-src");
}
n = i + 1;
}
}
}
</script>

使用节流函数进行性能优化

如果直接将函数绑定在scroll事件上,当页面滚动时,函数会被高频触发,这非常影响浏览器的性能。

我想实现限制触发频率,来优化性能。

节流函数:只允许一个函数在N秒内执行一次。下面是一个简单的节流函数:

1
2
// throttle函数省略
window.addEventListener('scroll',throttle(lazyload, 500, 1000));

IntersectionObserver来实现图片可视区域的懒加载

即使使用了函数节流,也会造成页面回流

1
2


其他

  • 利用雪碧图减少http请求
  • base64编码内联小图片
  • CDN加速

结语

性能优化需要了解其背后的原理,然后在项目中去实践,才能真正掌握。

文章摘自它出,初识前端性能优化,仅处于学习记录的目的,不做它用,自己再根据实践积累做修改和补充。

-------------要说再见啦感谢大佬的光临~-------------