• 原文地址:Of SVG, Minification and Gzip
  • 原文作者:Anton Khlynovskiy
  • 译文出自:掘金翻译计划
  • 本文永久链接:github.com/xitu/gold-m…
  • 译者:lsvih
  • 校对者:HuskyDoge, atuooo

从 Gzip 压缩 SVG 说起 — 论如何减小资源文件的大小

文件越小,意味着下载速度就越快。因此在向客户端发送资源文件前,使文件变得更小是件有益的事情。

其实,精简与压缩资源文件不仅是一件很棒的事情,同时也是每一位现代开发者应该尽量去做的事情。但是,用于精简的工具通常无法做到完美精简;用于压缩的压缩器效果好坏会取决于用于压缩的数据。下面介绍一些小技巧与方法,用于调整这些工具,使其达到最好的工作状态。

准备工作

我们将以一个简单的 SVG 文件为例:

这个<svg>图像的内容为一个 10x10 像素的区域(viewBox),其中包含了两个 6x6 的正方形(<rect>)。原始文件大小为 176 字节,经过 gzip 压缩过后大小为 138 字节。

当然这个图像并没有什么艺术感,但它足以满足这篇文章想要表达的意思,并且防止这篇文章变成长篇大论。

第 0 步:Svgo

运行 svgo image.svg 直接进行压缩。

(为了便于阅读,为其添加了回车与缩进)

可以明显地看到,rect 被替换成了 pathpath 路径形状由它的 d 属性定义,后面的一串命令类似于 canvas 的 draw 函数,控制一支虚拟的笔移动进行绘画。命令可以是绝对位移(移动 x,y),也可以是相对位移(向某方向移动 x,y)。请仔细观察其中的一条路径:

M 0 0:路径起点为坐标(0, 0) h 6:水平向右移动 6 px v 6:垂直向下移动 6 px H 0:水平移动至 x = 0 z:闭合路径 — 移回路径的起点

这个路径画出的正方形是多么的精确!而且它比 rect 元素更加的紧凑。

另外,#f00 被改成了 red,这儿也少了一个字节!

现在文件大小为 135 字节,gzip 压缩过后为 126 字节。

第 1 步:进行整体缩放

你可能已经注意到了,两个路径中的所有坐标均为偶数。我们是否可以把它们都除以 2 呢?

图像和之前看起来是一样的,但它缩小了两倍。因此,我们可以对 viewBox 进行缩放,使图像与之前一样大。

现在文件大小为 133 字节,gzip 压缩过后为 124 字节。

第 2 步:使用非闭合路径

回过头来看路径。两个路径中的最后一个命令都是 z,也就是“闭合路径”。但路径在填充的时候会被隐式地闭合,因此我们可以删除这些命令。

又少了 2 字节,现在文件大小为 131 字节,gzip 压缩过后为 122 字节。从常识上说,原始字节数越少,能压缩的大小也越小。而现在我们已经在 svgo 之后节省了 4 个 gzip 字节了。

你可能会想:为什么 svgo 不自动进行这些优化呢?原因是缩放图像与删除尾部的 z 命令是不安全的。请看下面的例子:

这是一些有 stroke(路径宽度)的图形。从左至右分别为:原始图形、不闭合的情况、不闭合且进行缩放的情况。

线宽完全混乱了。庆幸的是,我们知道自己不需要使用线宽。但是 Svgo 并不知道这个情况,因此它必须要保证图形的安全,避免不安全的变换。

现在看起来不能从代码中删除任何东西了。XML 语法是严格的,现在所有的属性都是必须的,并且它们的值不能不加引号。

你以为结束了?并不,这仅仅是个开始。

第 3 步:减少出现的字母

现在,让我来介绍一个非常方便的工具:gzthermal。它可以分析需要进行 gzip 压缩的文件,并对进行编码的原始字节进行着色。更好压缩的字节是绿色,不好压缩的数据是红色,简单明了。

请再次关注 d 属性,尤其是被标成红色的 M 命令值得注意。我们不能删除它,但我们可以用相对位移 m2 2 来代替它。

初始的“指针”位置为坐标轴原点(0, 0),因此移动(2, 2)和从原点移动(2, 2)是同一个意思。让我们试试:

原始文件依然是 131 字节,但是经过 gzip 压缩过后大小仅有 121 字节了。发生了什么?答案是……

哈夫曼树(Huffman Trees)

Gzip 使用的是 DEFLATE 压缩算法,而 DEFLATE 算法是以哈夫曼树为基础构建的。

哈夫曼编码的核心思想就是使用更少的比特对出现次数更多的符号进行编码,反之亦然,出现次数很少的符号需要占用更多的比特。

没错,这儿说的是比特不是字节。DEFATE 算法会将一字节的字符视为一系列的比特,无论一字节包含 7、9、100 个比特,DEFLATE 算法都能一视同仁。

以字符串“Test”为例,根据它出现的字母来进行编码: 00 T 01 e 10 s 11 t

对每个符号都进行过编码的字符串“Test”可以表示为:00011011,总共占 8 比特。

然后我们把它开头的“T”改成小写“test”,再试一次: 0 t 10 e 11 s

字母 t 出现了更多的次数,它的编码也变得更短,仅为 1 比特。这个字符串经过编码后为 010110,仅为 6 比特!


在我们的 SVG 中的 M 字母也一样。在将其变为小写之后,整个编码中都不包含大写的 M 了,可以将它从树上移除,因此平均编码长度可以更短。

当你编写对 gzip 友好的代码时,应该更多地使用那些使用频率较高的字符。即使你不能将代码长度减短,但它经过压缩后消耗的比特数也会变少。

第 4 步:回退引用(backreferences)

DEFLATE 算法还有一个特性:回退引用。某些编码点不会直接进行编码,而是告诉解码器复制一些最近解码的字节。

因此,它不需要对原始字节一次又一次地进行编码,而是可以直接引用: 向前返回 n 个字节,复制 m 个字节 例如:

Hey diddle diddle, the cat and the fiddle.

Hey diddle**<7,7>**, the cat and**<12,5>**f**<24,5>**.

巧妙的是,gzthermal 还有一种只显示回退引用的特殊模式。 gzthermal -z 会显示以下图像:

普通文本字节为橙色,可回退引用的字节为蓝色。下面的动画更直观:

除了 fill 值、m 命令和最后的 H 命令外,第二条路径几乎全部都使用了回退引用。对于 fill 和 m 我们无能为力,因为第二个方块的确有着不同的颜色和位置。

但是它们的形状是一样的,并且我们现在对 gzip 有了更加清晰的认识。因此,我们可以将绝对位移命令 H0H2 都替换为相对位移命令:h-3

现在,两个分开的回退引用合为了一个,文件大小为 133 字节,gzip 后的大小为 119 字节。虽然我们在压缩前增加了 2 个字节,但 gzip 的结果又减少了 2 个字节!

我们只需要关心压缩后的大小即可:在传送资源时,客户端 99.9% 用的是 gzip 或者 brotli。顺带说一下 brotli。

Brotli 压缩算法

Brotli 是于 2015 年推出的用于替换浏览器中 gzip(源自 1992)的算法。不过它与 gzip 在很多方面都有相似之处:它也是基于哈夫曼编码与回退引用的原理,因此我们前面为 gzip 所做的调整都可以同样利于 Brotli。最后让我们用 Brotli 应用于前面的所有步骤:

原始文件大小:106 字节 在第 0 步之后(svgo):104 字节 在第 1 步之后(viewBox):105 字节 在第 2 步之后(使用非闭合路径):113 字节 在第 3 步之后(小写 m):116 字节 在第 4 步之后(相关命令):102 字节

如你所见,最终的文件比 svgo 后的更小。这可以说明,之前我们为 gzip 做的酷炫的工作同样适用于 Brotli。

但是,中间步骤的文件大小却是混乱的,Brotli 压缩后的文件变得更大了。毕竟,Brotli 并不是 gzip,它是一种单独的新算法。尽管与 gzip 有一些相似之处,但仍有所不同。

其中最大的不同是,Brotli 内置了预定义字典,在编码时使用它进行上下文启发。此外,Brotli 的最小回退引用大小为 2 字节(gzip 仅能创建 3 字节及以上的回退引用)。

可以说,Brotli 比 gzip 更加难以预测。我很想解释一下是什么导致了“压缩退化”,可惜 Brotli 并没有类似于 gzip 的 gzthermal 和 defdb 之类的工具。我只能靠它的规范 以及试错的方法来进行调试。

试错法

让我们再试一次。这次将改变 fill 属性内的颜色。显然 red#f00 更短,但也许 Brotli 会用更长的回退引用进行压缩。

gzip 压缩过后大小为 120 字节,Brotli 压缩过后为 100 字节。gzip 流长了 1 字节,Brotli 流短了 2 字节。

此时,它在 Brotli 中表现更好,在 gzip 中表现更差。我觉得,这完全无碍!因为我们几乎不可能一次性将数据针对所有压缩器进行优化,并得到最佳结果。解决压缩器问题就像转一个糟糕的魔方,只能尽量优化。

总结

上面描述的所有的调整方法都不仅限于 SVG 压缩为 gzip 的情景。

以下是一些可以帮助你写出更具备压缩性能的代码的准则:

  1. 压缩更小的源数据可能会得到更小的压缩数据。
  2. 不同的字符越少就意味着熵越少。而熵越小,压缩效果就越好。
  3. 频繁出现的字符会以更小的字节被压缩。删除不常见字符以及使常见字符更常见可以提高压缩效率。
  4. 长段重复的代码可以被压缩成几个字节。DRY(“不要重复自己”原则)不一定在任何情况下都是最好的选择,有时候重复自己反而能得到更好的结果。
  5. 有些时候更大的源数据反而可以得到更小的压缩数据。减少熵可以让压缩器更好地移除冗余的信息。

你可以在 此 GitHub repo 中找到以上所有资源、压缩过的图片以及其它资料。

希望你喜欢这篇文章。下次我们将讨论如何压缩普通 JavaScript 代码与 Webpack bundle 中的 JavaScript 代码。


掘金翻译计划 是一个翻译优质互联网技术文章的社区,文章来源为 掘金 上的英文分享文章。内容覆盖 Android、iOS、前端、后端、区块链、产品、设计、人工智能等领域,想要查看更多优质译文请持续关注 掘金翻译计划、官方微博、知乎专栏。

从 Gzip 压缩 SVG 说起 — 论如何减小资源文件的大小相关推荐

  1. c#实现linux中gzip压缩解压缩算法:byte[]字节数组,文件,字符串,数据流的压缩解压缩

    全栈工程师开发手册 (作者:栾鹏) c#教程全解 c#实现gzip压缩解压缩byte[]字节数组,文件,字符串. 测试代码 static void Main() {//测试字符串String inpu ...

  2. java实现linux中gzip压缩解压缩算法:byte[]字节数组,文件,字符串,数据流的压缩解压缩

    全栈工程师开发手册 (作者:栾鹏) java教程全解 java实现gzip压缩解压缩byte[]字节数组,文件,字符串. 测试代码 public static void main(String[] a ...

  3. pdf文件怎么压缩得更小?如何改变pdf文件的大小?

    在平时需要pdf压缩大小的时候,通常我们会选择下载安装pdf压缩软件,今天小编就分享一款pdf在线压缩工具,通过浏览器压缩pdf文件大小,操作简单,处理速度快,下面一起来了解一下pdf压缩(https ...

  4. Nginx 笔记与总结(13)Nginx 的 gzip 压缩

    使用 FireFox(40.0)访问博客园(http://www.cnblogs.com/),观察 http 头信息 请求头信息: Accept-Encoding gzip, deflate 表示浏览 ...

  5. 启用Gzip压缩(IIS)提高客户端网站访问速度

    IIS上启用Gzip压缩(HTTP压缩) 详解 一.摘要 本文总结了如何为使用IIS托管的网站启用Gzip压缩, 从而减少网页网络传输大小, 提高用户显示页面的速度. 二.前言. 本文的知识点是从互联 ...

  6. Apache开启GZIP压缩功能方法

    Gzip是一种流行的文件压缩算法,现在的应用十分广泛,尤其是在Linux平台,本文讲解了如何开启Apache平台上的Gzip压缩功能 Gzip是一种流行的文件压缩算法,现在的应用十分广泛,尤其是在Li ...

  7. Nginx 启用 gzip 压缩

      Gzip是若干种文件压缩程序的简称,通常指GNU计划的实现,此处的 gzip 代表GNU zip.也经常用来表示 gzip 这种文件格式. 什么是 Gzip ? Gzip是若干种文件压缩程序的简称 ...

  8. Nginx开启Gzip压缩配置详解

    Nginx开启Gzip压缩配置详解 最近生产上发生了一些问题,原先所有的静态资源文件都是经过gzip压缩的,然而这几天突然都没有压缩了,经过一顿排查,发现是Nginx的配置有问题,借此机会详细了解了N ...

  9. linux上传网页文件大小,Apache启用GZIP压缩网页传输方法

    首先我们先了解Apache Gzip的相关资料. 一.gzip介绍 Gzip是一种流行的文件压缩算法,现在的应用十分广泛,尤其是在Linux平台.当应用Gzip压缩到一个纯文本文件时,效果是非常明显的 ...

最新文章

  1. 算法设计与分析第3章 贪心算法
  2. 腾讯云Linux云主机SSH远程连接
  3. java nio Selector (新IO)分析
  4. Hadoop之父Doug Cutting:Lucene到Hadoop的开源之路
  5. java9String类简单了解
  6. 信息安全系统设计基础第十二周学习总结
  7. Spring MVC的@PostMapping注解
  8. 为什么程序员总是打扮成这样一幅鬼样子
  9. Java对象运行时在内存中的情况
  10. 「ECharts」交互 API (echarts、echartsInstance)
  11. JVM调优思路、订单秒杀jvm调优案例
  12. 2月份全球制造业PMI为55.6% 已连续8个月保持在50%以上
  13. 力扣116. 填充每个节点的下一个右侧节点指针(JavaScript)
  14. python免费课程400节-Python2 教程
  15. linux下copy命令c实现,C语言自己实现linux下cp文件复制命令
  16. 第五章 APP元素定位
  17. Visual C++课程设计选题
  18. 如何复制出计算机缓存中的歌曲,怎么把哔哩哔哩里缓存的视频中的音频单独提取出来。...
  19. 机电传动控制第二周作业
  20. 2018 Github优秀开源项目整理

热门文章

  1. 关于javascript闭包中的this对象
  2. iOS - UIStoryboard
  3. 2014/08/24——升级stepbystep修复tc不刷新问题并加入杭电bc
  4. 前端见微知著工具篇:Bower组件管控
  5. HTML特殊字符过滤器
  6. 使用母版页后出现控件,使用FindControl找不到指定控件
  7. FreeTextBox License机制的粗浅分析
  8. python kafka 生产
  9. 想学Python,梦雅给你指一条明路!
  10. java 主动抛出 段错误_段错误产生的原因~