# 图片质量与性能优化
本篇分析各种图片的优缺点以及他们分别适用于那些场景:
# 二进制位数与色彩的关系
在计算机中,像素用二进制数来表示。不同的图片格式中像素与二进制位数之间的对应关系是不同的。一个像素对应的二进制位数越多,它可以表示的颜色种类就越多,成像效果也就越细腻,文件体积相应也会越大。
一个二进制位表示两种颜色(0|1 对应黑|白),如果一种图片格式对应的二进制位数有 n 个,那么它就可以呈现 2^n 种颜色。
# JPEG/JPG
关键字:有损压缩、体积小、加载快、不支持透明
# JPEG 的优点
JPEG 是有损压缩,它采用了高效的压缩算法,使它成为一种非常轻巧的图片格式(当我们把图片体积压缩至原有体积的 50% 以下时,JPEG 仍然可以保持住 60% 的品质),虽然是有损压缩,但是压缩后成像品质较高,是一种高质量的压缩方式。
# 使用场景
JPEG 适用于呈现色彩丰富的图片,在我们日常开发中,JPEG 图片经常作为大的背景图、轮播图或 Banner 图出现。两大电商网站对大图的处理,是 JPEG 图片应用场景的最佳写照。使用 JPEG 呈现大图,既可以保住图片的质量,又不会带来令人头疼的图片体积。
# JPEG 的缺陷
JPEG 处理矢量图形和 Logo 等线条感较强、颜色对比强烈的图像时,人为压缩导致的图片模糊会相当明显。此外,JPEG 图像不支持透明度处理,透明图片需要召唤 PNG 来呈现。
# PNG-8 与 PNG-24
关键字:无损压缩、质量高、体积大、支持透明
# PNG 的优点
PNG(可移植网络图形格式)是一种无损压缩的高保真的图片格式。8 和 24,这里都是二进制数的位数。按照我们前置知识里提到的对应关系,8 位的 PNG 最多支持 256 种颜色,而 24 位的可以呈现约 1600 万种颜色。
PNG 图片具有比 JPG 更强的色彩表现力,对线条的处理更加细腻,对透明度有良好的支持。它弥补了上文我们提到的 JPG 的局限性,唯一的 BUG 就是体积太大。
# PNG-8 与 PNG-24 的选择题
什么时候用 PNG-8,什么时候用 PNG-24,这是一个问题。
理论上来说,当你追求最佳的显示效果、并且不在意文件体积大小时,是推荐使用 PNG-24 的。
但实践当中,为了规避体积的问题,我们一般不用 PNG 去处理较复杂的图像。当我们遇到适合 PNG 的场景时,也会优先选择更为小巧的 PNG-8。
如何确定一张图片是该用 PNG-8 还是 PNG-24 去呈现呢?好的做法是把图片先按照这两种格式分别输出,看 PNG-8 输出的结果是否会带来肉眼可见的质量损耗,并且确认这种损耗是否在我们(尤其是你的 UI 设计师)可接受的范围内,基于对比的结果去做判断。
# 应用场景
前面我们提到,复杂的、色彩层次丰富的图片,用 PNG 来处理的话,成本会比较高,我们一般会交给 JPG 去存储。
考虑到 PNG 在处理线条和颜色对比度方面的优势,我们主要用它来呈现小的 Logo、颜色简单且对比强烈的图片或背景等。
# SVG
关键字:文本文件、体积小、不失真、兼容性好
SVG(可缩放矢量图形)是一种基于 XML 语法的图像格式。它和本文提及的其它图片种类有着本质的不同:SVG 对图像的处理不是基于像素点,而是是基于对图像的形状描述。
# SVG 的优点
- 与 PNG 和 JPG 相比,文件体积更小,可压缩性更强。
- 可无限放大而不失真,能够适配多种分辨率。
# 应用场景
SVG 是文本文件。我们既可以像写代码一样定义 SVG,把它写在 HTML 里、成为 DOM 的一部分,也可以把对图形的描述写入以 .svg 为后缀的独立文件(SVG 文件在使用上与普通图片文件无异)。这使得 SVG 文件可以被非常多的工具读取和修改,具有较强的灵活性。
通常在做矢量图的时候,SVG 是一个比较好的选择。
# SVG 的缺点
SVG 的局限性主要有两个方面:
- 一方面是它的渲染成本比较高,这点对性能来说是很不利的。
- 另一方面,SVG 存在着其它图片格式所没有的学习成本(它是可编程的)。
# BASE64
关键字:文本文件、依赖编码、小图标解决方案
# 理解 Base64
Base64 并非一种图片格式,而是一种编码方式。
Base64 是一种用于传输 8Bit 字节码的编码方式,通过对图片进行 Base64 编码,我们可以直接将编码结果写入 HTML 或者写入 CSS,这样能够减少 HTTP 请求的次数。
雪碧图的思想也是类似的。雪碧图或者叫图像精灵(sprite,意为精灵),被运用于众多使用大量小图标的网页应用之上。它可取图像的一部分来使用,使得使用一个图像文件替代多个小文件成为可能。相较于一个小图标一个图像文件,单独一张图片所需的 HTTP 请求更少,对内存和带宽更加友好。
# Base64 的应用场景
Base64 编码后,图片大小会膨胀为原文件的 4/3,如果对大图进行编码,即便我们减少了 HTTP 请求,也无法弥补这庞大的体积带来的性能开销,得不偿失。
在传输非常小的图片的时候,Base64 带来的文件体积膨胀、以及浏览器解析 Base64 的时间开销,与它节省掉的 HTTP 请求开销相比,可以忽略不计,这时候才能真正体现出它在性能方面的优势。
因此,通常满足以下条件时适合使用 Base64 编码:
- 图片的实际尺寸很小(小于 2KB)
- 图片无法以雪碧图的形式与其它小图结合(合成雪碧图仍是主要的减少 HTTP 请求的途径,Base64 是雪碧图的补充)
- 图片的更新频率非常低(不需我们重复编码和修改文件内容,维护成本较低)
# Base64 编码工具推荐
这里最推荐的是利用 webpack 来进行 Base64 的编码——webpack 的 url-loader (opens new window) 非常聪明,它除了具备基本的 Base64 转码能力,还可以结合文件大小,帮我们判断图片是否有必要进行 Base64 编码。
除此之外,市面上免费的 Base64 编解码工具种类是非常多样化的,有很多网站都提供在线编解码的服务,大家选取自己认为顺手的工具就好。
# WebP
关键字:年轻的全能型选手
WebP 于 2010 年被提出, 是 Google 专为 Web 开发的一种旨在加快图片加载速度的图片格式,它支持有损压缩和无损压缩。
# WebP 的优点
WebP 的官方介绍:与 PNG 相比,WebP 无损图像的尺寸缩小了 26%。在等效的 SSIM 质量指数下,WebP 有损图像比同类 JPEG 图像小 25-34%。
无损 WebP 支持透明度(也称为 alpha 通道),仅需 22% 的额外字节。对于有损 RGB 压缩可接受的情况,有损 WebP 也支持透明度,与 PNG 相比,通常提供 3 倍的文件大小。
# WebP 的局限性
目前市场上浏览器对 WebP 的支持还不够友好,点击查看 (opens new window)。只有小部分浏览器的一些版本支持。
此外,WebP 还会增加服务器的负担——和编码 JPG 文件相比,编码同样质量的 WebP 文件会占用更多的计算资源。
# WebP 的应用场景
现在限制我们使用 WebP 的最大问题不是“这个图片是否适合用 WebP 呈现”的问题,而是“浏览器是否允许 WebP”的问题,即我们上文谈到的兼容性问题。具体来说,一旦我们选择了 WebP,就要考虑在 Safari 等浏览器下它无法显示的问题,也就是说我们需要准备 PlanB,准备降级方案。
一般有 2 种降级的思路:
- 程序根据浏览器的型号、以及该型号是否支持 WebP 这些信息来决定当前页面中图片地址使用的的是
.webp
后缀还是.jpg
后缀。在浏览器环境支持 WebP 的情况下,优先使用 WebP 格式,否则就把图片降级为 JPG 格式(本质是对图片的链接地址作简单的字符串切割)。 - 由服务器根据 HTTP 请求头部的 Accept 字段来决定返回什么格式的图片。当 Accept 字段包含 image/webp 时,就返回 WebP 格式的图片,否则返回原图。这种做法的好处是,当浏览器对 WebP 格式图片的兼容支持发生改变时,我们也不用再去更新自己的兼容判定代码,只需要服务端像往常一样对 Accept 字段进行检查即可,维护性更强、更加灵活。