前端图片性能优化方案总结

1,110 阅读11分钟

一、背景

最近发现把自己项目部署到服务器上面后去方面,登录页的logo图片加载有点久,这样的用户体验并不好,所以想着怎么去优化并且总结图片优化的方案。

image.png

二、方案

1. 图片格式

来自菜鸟教程对于一些主流图片格式的一些信息:

图片格式压缩方式透明度动画浏览器兼容适应场景
JPEG有损压缩不支持不支持所有复杂颜色及形状、尤其是照片
GIF无损压缩支持支持所有简单颜色,动画
PNG无损压缩支持不支持所有需要透明时
APNG无损压缩支持支持Firefox Safari iOS Safari需要半透明效果的动画
WebP有损压缩支持支持Chrome Opera Android Chrome Android Browser复杂颜色及形状 浏览器平台可预知
SVG无损压缩支持支持所有(IE8以上)简单图形,需要良好的放缩体验 需要动态控制图片特效

详细描述如下:

JPEG

JPEG是目前互联网上最常用的图像文件类型之一,因为它的可压缩性及浏览器/操作系统广泛支持。

大多数社交媒体平台(例如Facebook和Instagram)会自动将上传的图像文件转换为JPEG,且会根据不同场景使用固定的尺寸大小来控制图片的分辨率。

优点:浏览器兼容性好,文件体积小

缺点:有损压缩可能导致较差的文本可读性,也不支持透明度等

GIF

优点:浏览器兼容好,可以动画形式呈现

缺点:缺点-8-bit限制代表图像质量受限

PNG

web广泛使用的图像格式。

优点:浏览器兼容性好,高质量(无损)图像和清晰可见的文本,支持透明度

缺点:如果文件过多(特别是高分辨率图像),可能会严重影响网站加载速度

WebP

JPEG和PNG最佳替代图像格式

优点:较小的文件即可获得相同或更好的图像质量

缺点:并非所有浏览器和图像编辑器都支持

SVG

可伸缩矢量图形文件格式(通常称为SVG),由W3C开发的一种标记语言,可以在浏览器中直接渲染为二维图像。它不像栅格格式那样依赖像素,而是使用XML文本以类似于数学方程式计算勾勒形状和线条获得图形。也就是说,可以在不损失任何质量的情况下无限放大SVG图像。

优点:兼容性好,体积小,无损缩放

缺点:对于图像或复杂图形而言,不是理想的格式

二、图片压缩

有损压缩无损压缩都是属于压缩技术,但不管是采用何种技术模型,两者的本质内容都是一样的,即都是通过某种特殊的编码方式将数据信息中存在的重复度、冗余度有效地降低,从而达到数据压缩的目的。

有损压缩与无损压缩的区别

还原性

无损压缩:可以完全还原的

有损压缩:还原后不能和原来的文件一样,有一定的损耗的

压缩率

无损压缩:压缩率是受到数据统计冗余度的理论限制,一般为2:1到5:1

有损压缩:利用了人类对图像或声波中的某些频率成分不敏感的特性,允许压缩过程中损失一定的信息,虽然不能完全恢复原始数据,但是所损失的部分对理解原始图像的影响缩小,却换来了大得多的压缩比最高可达200:1甚至更多

压缩格式

有损压缩:JPEG、WebP等

无损压缩:GIF、PNG、SVG等

压缩原理

有损压缩两种的基本机制:

  • 一种是有损变换编解码,首先对图像或者声音进行采样、切成小块、变换到一个新的空间、量化,然后对量化值进行熵编码。
  • 另外一种是预测编解码,先前的数据以及随后解码数据用来预测当前的声音采样或者图像帧,预测数据与实际数据之间的误差以及其它一些重现预测的信息进行量化与编码。

无损压缩:原理有行程编码、霍夫曼编码和算术编码等。

应用领域

有损压缩广泛应用于语音,图像和视频数据的压缩

无损压缩受压缩比的限制暂时只用于文本数据,程序和特殊应用场合的图像数据(如指纹图像,医学图像等)的压缩

压缩实现

工具手动压缩

网上有很多的工具,就不一一阐述了

推荐:

  • tinypng png 压缩率 66%;网站;一次可提供20张图片压缩,提供开发API需要申请免费key,每月可压缩500张图片。
  • 优图压缩 png 压缩率 -6%;网站;惊悚,压缩之后变大了。

image.png

工程化压缩

例如webpack、vite、gulp等工具打包时候进行压缩,都会有相应的配置方式,具体网上有很多的教程

  • image-webpack-loader 基于webpack的图片压缩工具,压缩方案采用 imagemin ,代码实现比较全面,支持 Minify PNG, JPEG, GIF, SVG and WEBP images 等,支持各个版本 webpack,github start 1625
  • imagemin-webpack-plugin 同上,写法和用法略有不同,github start 561
  • gulp-imageisux 来源于腾讯智图提供,其实是在线压缩,所有图片都要上传;github start 370
  • vite-plugin-imagemin

三、使用DATA URL(即图片转化BASE64编码)

什么是DATA URL

Data URL,是以data:模式为前缀的URL,允许内容的创建者将较小的文件嵌入到文档中。与常规的URL使用场合类似

Data URLdata:前缀、MIME类型(表明数据类型)、base64标志位(如果是文本,则可选)以及数据本身四部分组成。

data:[][;charset=][;base64],

优缺点

优点:

  • 当访问外部资源很麻烦或受限时,可以将外部资源转为Data URL引用
  • 当图片是在服务器端用程序动态生成,每个访问用户显示的都不同时,这是需要返回一个可用的URL
  • 当图片的体积太小,占用一个HTTP会话不是很值得时

缺点:

虽然Data URL允许使用者将文件嵌入到文档中,这在某些场景下较为合适,但是Data URL也有一些缺点:

  • 体积更大:Base64编码的数据体积通常是原数据的体积4/3,也就是Data URL形式的图片会比二进制格式的图片体积大1/3
  • 不会缓存:Data URL形式的图片不会被浏览器缓存,这意味着每次访问这样的页面时都被下载一次。这是一个使用效率方面的问题——尤其当这个图片被整个网站大量使用的时候。

获取base64代码

使用原生Web API编码/解码

Javascript中有两个函数负责编码和解码base64字符串,分别是atobbtoa。 两者都只针对Data URL中的data进行处理。

btoa('hello base64') // "aGVsbG8gYmFzZTY0"atob('aGVsbG8gYmFzZTY0') // "hello base64"
  • atob(): 负责解码已经使用base64编码了的字符串。
  • btoa(): 将二进制字符串转为base64编码的ASCII字符串。

Canvas的toDataURL方法

Canvas提供了toDataURL方法,用于获取canvas绘制内容,将其转为base64格式。 如下图所示,文本框中的内容即为canvas中绘制内容的base64格式。

外部工具转化

图片转 BASE64 编码 | 菜鸟工具 (runoob.com)

Base64的使用

Base64在CSS中的使用

.demoImg{ background-image: url("data:image/jpg;base64,/9j/4QMZRXhpZgAASUkqAAgAAAAL...."); }

Base64在HTML中的使用

<img width="40" height="30" src="data:image/jpg;base64,/9j/4QMZRXhpZgAASUkqAAgAAAAL...." />

四、配置CDN

什么是CDN

百度百科:CDN的全称是Content Delivery Network,即内容分发网络。CDN是构建在现有网络基础之上的智能虚拟网络,依靠部署在各地的边缘服务器,通过中心平台的负载均衡、内容分发、调度等功能模块,使用户就近获取所需内容,降低网络拥塞,提高用户访问响应速度和命中率。CDN的关键技术主要有内容存储和分发技术。

简单的说,CDN 的工作原理就是将您源站的资源缓存到位于全球各地的 CDN 节点上,用户请求资源时,就近返回节点上缓存的资源,而不需要每个用户的请求都回您的源站获取,避免网络拥塞、缓解源站压力,保证用户访问资源的速度和体验。

什么是图片CDN

它是专门为图片做优化的,通常包含缩放、格式转换等。你可以把它看成是一个API,通过传入尺寸、质量、格式等参数,获取到对应的图片内容。这也使得我们在使用上非常方便,适用于多种不同的场景。

一般采用第三方云服务的图片CDN,易于配置,功能强大。

例如:

image.png

五、图片懒加载

image.png

现在大多数浏览器是支持img loading的

各种ui组件也有对应的lazy属性

image.png

如果对原生懒加载有兴趣可以自行学习一下

大概思路就是:

1.img 标签不放 src 属性,而是放诸如data-src这样的自定义属性,把图片路径放在这个属性下。

2.当图片进入可视区域时,把 data-src 的图片路径拿出来放置到 src 属性上。

3.浏览器识别到 img 属性的 src 属性,触发重渲染,显示出图片。

六、图片预加载

什么是预加载

提前加载图片,加载完毕后会缓存到本地,当用户需要查看时可直接从本地缓存中渲染,拥有良好的用户体验。

css实现

body:after {
    content: "";
    display: block;
    position: absolute;
    background: url("../image/manage/help/help_item2_01.png?v=201707241359") no-repeat -10000px -1000px,url("../image/manage/help/help_item2_02.png?v=201707241359") no-repeat -10000px -1000px,url("../image/manage/help/help_item2_03.png?v=201707241359") no-repeat -10000px -1000px,url("../image/manage/help/help_item2_04.png?v=201707241359") no-repeat -10000px -1000px,url("../image/manage/help/help_item2_05.png?v=201707241359") no-repeat -10000px -1000px,url("../image/manage/help/help_item2_06.png?v=201707241359") no-repeat -10000px -1000px,url("../image/manage/help/help_item2_07.png?v=201707241359") no-repeat -10000px -1000px,url("../image/manage/help/help_item2_01.png?v=201707241359") no-repeat -10000px -1000px;
    width: 0;
    height: 0
}

原理是加载了该图片,但是我们不显示在可视范围内。这种方式极其简单。但是也有一个致命的弱点,图片跟随文档一同加载,有时候我们为了提高文档的加载速度,那么这种方式方式就不适合了。

JS实现

<template>
  <div>
    <img :src="imgData[imgInd]" alt="" />
  </div>
  <button @click="onLeft">Left</button>
  <button @click="onRight">Right</button>
</template>

<script lang="ts">
import { ref } from "vue";
export default {
  name: "App",
  setup() {
    let imgData = ref<string[]>([
      "https://cdn.jsdelivr.net/gh/lztnb/img@master/3.jpg",
      "https://cdn.jsdelivr.net/gh/lztnb/img@master/4.jpg",
      "https://cdn.jsdelivr.net/gh/lztnb/img@master/5.jpg",
      "https://cdn.jsdelivr.net/gh/lztnb/img@master/7.jpg",
    ]);
    // 预加载图片
    function preloadImg(srcArr: string[]): void {
      if (srcArr instanceof Array) {
        for (var i = 0; i < srcArr.length; i++) {
          var oImg = new Image();
          oImg.src = srcArr[i];
        }
      }
    }
    preloadImg(imgData.value);

    let imgInd = ref<number>(0);
    function onLeft() {
      imgInd.value - 1 < 0 ? 0 : --imgInd.value;
    }
    function onRight() {
      imgInd.value + 1 == imgData.value.length
        ? imgData.value.length - 1
        : ++imgInd.value;
    }
    return {
      onLeft,
      onRight,
      imgInd,
      imgData,
    };
  },
};
</script>

推荐:从理论到代码实操,图片预加载和懒加载 - 掘金 (juejin.cn)

七、响应式图片

什么是响应式图片

在不同分辨率的设备上显示不同尺寸的图片,避免资源的浪费。

常用的方法就是 css3 的媒体查询(media query)。

@media  screen and (min-width: 1200px) {
  img {
    background-image: url('1.png');
  }
}
@media  screen and (min-width: 992px) {
  img {
    background-image: url('2.png');
  }
}
@media  screen and (min-width: 768px) {
  img {
    background-image: url('3.png');
  }
}
@media screen and (min-width: 480px) {
  img {
    background-image: url('4.png');
  }
}

还可以使用 HTML5 的 picture 属性进行响应式处理。但不是所有浏览器对于 picture 这个标签支持。

<picture>
  <source srcset="src/img/l.png" media="(min-width: 1200px)" />
  <source srcset="src/img/2.png" media="(min-width: 992px)" />
  <source srcset="src/img/4.png" media="(min-width: 768px)" />
  <img src="src/img/4.png" />
</picture>

八、渐进式图片

渐进式图片渲染过程中,会先显示整个图片的模糊轮廓,随着扫描次数的增加,图片变得越来越清晰。这种格式的主要优点是在网络较慢的情况下,可以看到图片的轮廓知道正在加载的图片大概是什么。

渐进式图片其实是一种JPEG格式的图片,和普通的JPEG图片的区别如下:

  • 普通图片渲染时,数据将按照存储时的顺序从上到下逐行扫描被显示出来的,直到所有的数据都被读取完毕,就完成了整张图片的显示。
  • 渐进式图片渲染过程中,会先显示整个图片的模糊轮廓,随着扫描次数的增加,图片变得越来越清晰。这种格式的主要优点是在网络较慢的情况下,可以看到图片的轮廓知道正在加载的图片大概是什么。

三、最后

优化成功!

image.png

参考资料:

juejin.cn/post/696576…

www.runoob.com/w3cnote/web…

www.wbolt.com/in-depth-di…

juejin.cn/post/684490…

c.runoob.com/front-end/5…

CDN是什么?为什么使用CDN? - 掘金 (juejin.cn)

juejin.cn/post/685457…

原生实现图片懒加载 - 掘金 (juejin.cn)

从理论到代码实操,图片预加载和懒加载 - 掘金 (juejin.cn)