一条高效传递信息的原则:字不如表,表不如图。
图像资源优化的根本思想:压缩。无论选取何种图像的文件格式,还是针对同一种格式压缩至更小的尺寸,其本质都是用更小的资源开销来完成图像的传输和展示。
3.1 图像基础
3.1.1 图像是否必须
如果一个页面打开后有很多图像,那么用户其实很难快速梳理出有效信息,即便获取到了也会让用户感觉很累。
当确定了图像的展示效果必须存在时,在前端实现上也并非一定就要用图像文件,还存在一些场景可以使用更高效的方式来实现所需的效果。
- 网站中一个图像在不同的页面或不同的交互状态下,需要呈现出不同的效果没有必要准备多份图像文件,只需用CSS将一张图像处理为不同效果即可。
- 如果图像上需要显示文字,建议使用前端代码添加,而不是使用带文字的图像。
3.1.2 矢量图和位图
- 矢量图:SVG是一种基于XML的图像格式,几乎所有浏览器都支持SVG。
- 位图:位图是通过对一个矩阵中的栅格进行编码来表示图像的,每个栅格只能编码标识一个特定的颜色,如果组成图像的栅格像素点越多且每个像素点能表示的颜色范围越广,则位图图像整体的显示效果就会越逼真。
3.1.3 分辨率
- 分辨率分为屏幕分辨率和图像分辨率。
- 用于插入图像的img标签,有一个srcset属性可以用来针对不同设备,提供不同分辨率的图像文件。
- 使用picture标签则会在多图像文件选择时,获得更多的控制维度,比如屏幕方向、设备大小、屏幕分辨率等。
3.1.4 压缩的有损和无损
压缩时降低源文件大小的有效方式,对JavaScript代码或网页的一些脚本文件而言,压缩掉的内容是一些多余的空格及不影响执行的注释,其目的是在不损坏正常执行的情况下,尽量缩小源文件的大小。对图像文件而言,由于人眼对不同颜色的敏感度存在差异,所以便可通过减少对某种原色的编码位数来减小文件大小,甚至还可以损失部分源文件信息,以达到近似的效果,使得压缩后的文件尺寸更小。
关于压缩技术的选型可以简单分两步:
- 首先确定业务所要展示的颜色阶数、图像显示的分辨率及清晰度,如果获取的图像源文件响应参数过高,便可适当进行有损压缩降低图像文件大小。(非必须)
- 确定了展示图像的质量后,便可利用无损压缩技术尽可能降低图像大小(必须,最好能通过完整的方案自动化执行)。
3.2 图像格式
3.2.1 JPEG
jpeg是一种有损压缩算法,它通过去除相关冗余图像和色彩数据等方式来获得较高的压缩率,同时还能展现出相当丰富的图像内容。
由于是有损压缩,当处理Logo或图标时,需要较强线条感和强烈颜色对比,jpeg图像可能会出现一些边界模糊的不加体验,另外jpeg图像不支持透明图。
jpeg常用的压缩编码方式:
- 压缩模式
jpeg包含了多种压缩模式,常见的有基于基线的、渐进式的。基线模式的jpeg加载顺序时自上而下的,渐进模式时将图像文件分为多次扫描,首先展示一个低质量模糊图像,随着扫描到的图像信息越多,图象清晰度不断提升。 - 渐进式jpeg的优缺点
渐进式的优点在于可以在网络连接缓慢的情况下,首先能快速加载出一个图像质量比较模糊的预览版本,用户可据此了解图像的大致内容,来决定是否继续花费时间等待完整图像的加载,用户体验相较基线式更好。
通过了解两种压缩的原理可以发现,渐进式jpeg的解码速度会比基线的要慢一些,因为增加了重复的检索开销。另外,通过渐进式jpeg压缩模式得到的图像文件也不一定是最小的。
故此,是否要采用渐进式jpeg需要综合考虑文件大小、大部分用户的设备类型与网络延迟。 - 其他jpeg编码方式
除了常见的基线与渐进式编码方式,最近还出现了几种现代的jpeg编码器,在尝试以更高的保真度及压缩后更小的文件大小为目标的同时还兼容当前主流的浏览器。代表:MozJPEG和Guetzli。
MozJPEG引入了对逐行扫描的优化及一些栅格量化的功能,最多能将图像文件压缩10%,而Guetzli则是找到人眼感知上无法区分的最小体积的JPEG。
可以借助结构相似性分数(SSIM)和一种基于人类感知测量图像的差异模型(Butteraugli)评价两者效果
// 其它JPEG编码方式
const imageminMozJPEG = require('imagemin-mozjpeg'); //引入MozJPEG依赖包
const imageminGuetzli = require('imagemin-guetzli'); //引入Guetzli依赖包
// MozJPEG压缩编码
gulp.task('mozjpeg', () =>
gulp.src('image/*.jpg')
.pipe(imagemin([
imageminMozJPEG({ quality: 85 })
]))
.pipe(gulp.dest('dist'))
)
// Guetzli压缩编码
gulp.task('guetzli', () =>
gulp.src('image/*.jpg')
.pipe(imagemin([
imageminGuetzli ({ quality: 85 })
]))
.pipe(gulp.dest('dist'))
)
MozJPEG:
原图大小982KB | Q=90/841KB | Q=85/562KB | Q=75/324KB |
---|---|---|---|
SSIM | 0.999936 | 0.999698 | 0.999478 |
Butteraugli | 1.576957 | 2.483837 | 3.66127 |
Guetzli:
原图大小982KB | Q=100/945KB | Q=90/687KB | Q=85/542KB |
---|---|---|---|
SSIM | 0.999998 | 0.99971 | 0.999508 |
Butteraugli | 0.40884 | 1.580555 | 2.0996 |
建议:
- 使用一些外部工具找到图像的最佳表现质量后,再用MozJPEG进行编码压缩。
- Guetzli会获得更高质量的图像,压缩速度相对较慢。
3.2.2 GIF
由于对支持颜色数量的限制,256色远小于展示照片所需颜色的数量级,所以GIF不适合用来呈现照片,可能用来呈现图标或Logo更好。
- 单帧的GIF转换为PNG
可以使用ImageMagick检查GIF图像文件,若不包含多帧动画则可使用ImageMagick工具将其转化为更适合展示图形的PNG文件格式。
// 单帧的GIF转化为PNG
const im = require('imagemagick');
// 检查是否为动画
im.identify(['-format', '%m', 'my.gif'], (err, output) => {
if(err) throw err;
// 通过output处理判断流程
})
// 将gif转化为png
im.convert(['my.gif', 'my.png'], (err, stdout) => {
if(err) throw err;
console.log('转化完成', stdout);
})
- GIF动画优化
可借助gifsicle实现将动画里连续帧中重复的像素信息去除。
// GIF动画优化
const { execFile } = require('child_process');
const gifsicle = require('gifsicle');
execFile(gifsicle, ['-o', 'output.gif', 'input.gif'], err => {
console.log('动画压缩完成');
});
- 用视频替代动画
建议将内容较长的GIF动画转化为视频后插入,因为动画也是视频的一种,成熟的视频编码格式可以让传输的动画内容节省网络贷款开销。
可用工具:ffmpeg。
3.2.3 PNG
PNG是一种无损压缩的高保真图片格式,支持透明度。唯一的不足就是文件体积太大。
- 对比GIF
PNG-8称为调色板PNG,除了不支持动画,其他所有GIF拥有的功能它都有,还支持alpha通道透明。只要不是颜色数量特别少的图像,PNG-8的压缩比表现都会更好。
优化建议:在使用单帧图像时,应尽量用PNG-8格式来替换GIF格式。 - 对比JPEG
当处理图像中的颜色超过256钟时,就需要用到JPEG或者真彩PNG,真彩PNG包括PNG-24、PNG-32,二者区别在于是否包括8位的alpha透明通道。
JPEG是有损的,拥有更高的压缩比。如果还是要用PNG,则可能在清晰的颜色过度周围出现不可接受的大色块。 - 优化PNG
PNG图像还有一个优点--------便于扩展。它将图像信息保存在“块”中,开发者可以通过“块”自定义一些额外的功能(并非所有软件都能读取识别)。
可引入imagemin-pngcrush工具对PNG文件进行相关优化。
// 优化PNG
const imagemin = require('imagemin');
const imageminPngcrush = require('imagemin-pngcrush');
imagemin(['images/*.png'], 'build/images', {
plugins: [imageminPngcrush()]
}).then(() => console.log('完成图像优化'))
3.2.4 WebP
- WebP的优缺点
WebP是Google在2010年推出的一种图像文件格式,目标是以较高的视觉体验为前提的,尽可能地降低有损压缩和无损压缩后的文件尺寸,同时还支持透明度和动画。根据WebP官方给出的数据,当使用WebP有损文件时,文件尺寸会比JPEG小25%~34%,而使用WebP无损文件时,文件尺寸会比PNG小26%。
兼容性问题:除了IE和苹果的Safari浏览器不支持,其他主流浏览器的最新版本都已支持WebP。
此外,由于有损压缩WebP使用了VP8视频关键帧编码,可能对较高质量(8099)的图像编码来说,会比JPEG占用更多的计算资源,但在较低质量(050)时,依然有很大的优势。 - 如何使用WebP
可以使用图像编辑软件直接导出WebP格式的图像文件,或使用webp-loader将原有的JPEG或PNG转化为WebP格式。
注意:尽量不要使用低质量的JPEG进行WebP格式转化,因为低质量的JPEG中可能包含压缩的伪像,影响最终效果。所以建议使用质量最佳的。 - 兼容性处理
通常的处理思路封为两种:
一种是前端处理浏览器兼容性的判断,可以通过浏览器的全局属性window.navigator.userAgent获取版本信息,再根据兼容支持情况判断是否请求WebP图像资源;也可以使用<picture>
标签来选择显示的图像格式。
另一种是将判断浏览器是否支持的工作放在后端处理,让服务器根据HTTP请求头的Accept字段来绝ing返回图像的文件格式。
3.2.5 SVG
SVG对图像的处理不是基于像素栅格的,而是通过图像的形状轮廓、屏幕位置等信息进行描述的。
- 优缺点
SVG这种基于XML语法描述图像形状的文件格式,适合用来标识Logo等图标图像,它可以无限放大且不失真;作为文本文件,还可以吧对图标图像的描述信息写在以.svg为后缀的文件中进行存储和引用。由于文本文件的高压缩比,最后的图像文件体积也会更小。
缺点与不足在于仅能表示矢量图,还有使用的学习成本和渲染成本比较高。 - 优化建议
(1) 应保持SVG尽量精简,去除可能携带的一些冗余信息,如注释、隐藏图层及元数据等。
(2) 由于显示器的本质是元素点构成位图,所以在渲染绘制矢量图时,会比位图的显示多一步光栅化的过程。建议使用预定义的SVG形状来代替自定义路径,如:<circle>
、<rect>
、<line>
、<polygon>
等。
(3) 尽量少用曲线。
(4) 不要在SVG中嵌入位图。
(5) 使用工具优化SVG(svgo)。
(6) 在优化过后,使用gzip压缩或brotli压缩。
3.2.6 Base64
无须发起任何关于图像url的请求,这是Base64的优点,同时也隐含了对于使用的限制。一般经过Base64编码后的图像大小,会膨胀四分之三。
使用条件:
- 图像文件的实际尺寸是否很小。
- 图像文件是否真的无法以雪碧图的形式进行引入。
- 图像文件的更新频率是否很低,避免在使用Base64时,增加不必要的维护成本。
3.2.7 格式选择建议
3.3 使用建议
3.3.1 CSS Sprite
CSS Sprite技术就是常说的雪碧图,通过将多张小图拼接成一张大图,有效地减少HTTP请求次数以达到加速显示内容的技术。
通常对于雪碧图的使用场景应满足以下条件:首先这些图标不会随用户信息变化而变化,属于静态图标;同时单张体积要尽量小,这样性能的提升才会比较乐观;若加载量比较大则效果会更好。
不建议将较大的图片拼接成雪碧图,因为这样拼接后的单个文件会非常大,占用网络带宽的增加与请求完成所耗费时间的延长,会完全淹没通过减少HTTP请求次数所带来的性能提升。
在HTTP1.x环境下,它确实能够减少相应的HTTP请求,**但需要注意当部分图标变更时,会导致已经加载的雪碧图缓存失效。**同时在HTTP2中,最好的方式应该是加载单张图像文件,因为可以在一个HTTP连接上发起多次请求,所以对于是否使用此方法,需要考虑具体的使用环境和网络设置。
3.3.2 Web字体
使用Web字体的优点:增强网站的设计感、可读性,同时还能搜索和选取所表示的文本内容,且不受屏幕尺寸与分辨率的影响,能提供一致的视觉体验。由于每个字型都是特定的矢量图标,所以可以将项目中用到的矢量图标打包到一个Web字体文件中使用,减少HTTP请求次数。
- 字体的使用
常用的字体格式有:EOT、TTF、WOFF、WOFF2,由于存在兼容性的问题,没有哪一种字体能适用所有浏览器,所以实际使用中,网站开发者会声明提供字体的多种文件格式,来达到一致性的体验效果。 - 子集内嵌
如果将所有字型都打包成一个文件来请求使用,不免会产生带宽浪费。字体文件能否按需加载,就成为了一个显而易见的优化项,即子集内嵌。
通过@font-face和unicode-range属性就可以定义所使用的字体子集,属性unicode-range用来指定所需字体在@font-face声明字体集中的子集范围,它支持三种形式:单一取值(如U+233)、范围取值(如U+233-2ff)、通配符范围(如U+2??),取值的含义是字体集文件中的代码索引点。
不过属性unicode-range也存在兼容性的问题,对于不支持的浏览器,若想提供必要的子集字体支持,则可能需要手动处理字体文件。 - 字体文件预加载
默认情况下,构建渲染树之前会阻塞字体文件的请求,这将可能导致部分文本渲染延迟,对此我们可以使用<link rel="preload">
对字体资源进行预加载。
<link rel="preload"
>需要和@font-face对字体的定义一同使用,它只负责提示浏览器需要预加载给定的资源。
注意:这样做将会无条件向网络发出字体请求,如果项目迭代将原本使用的字体文件修改或删除,也需要同步修改对字体预加载的设置。
3.3.4 注意display:none的使用
<div style="display: none;">
<img src="img.jpg" alt="img">
</div>
这种写法img的图像文件虽然被隐藏但依然会被请求。
<div style="display: none;">
<div style="background: url(img.jpg);"></div>
</div>
这样的写法CSS解析后发现父级使用了display:none,就不会计算子级的样式,故此不会去下载子级div的背景图像。
3.4 小结
- 适合用矢量图的地方首选矢量图。
- 使用位图时首选WebP,对不支持的浏览器场景进行兼容处理。
- 尽量为位图图像格式找到最佳质量设置。
- 删除图像文件中多余的元数据。
- 对图像文件进行必要的压缩。
- 为图像提供多种缩放尺寸的响应式资源。
- 对工程化通用图像处理流程尽量自动化。
前端性能优化系列:
前端性能优化:1.什么是前端性能优化
前端性能优化:2.前端页面的生命周期
前端性能优化:3.图像资源优化
前端性能优化:4.资源加载优化
前端性能优化:5.高性能的JavaScript代码
前端性能优化:6.项目构建优化
前端性能优化:7.页面渲染优化