- 原文地址:Of SVG, Minification and Gzip
- 原文做者:Anton Khlynovskiy
- 譯文出自:掘金翻譯計劃
- 本文永久連接:github.com/xitu/gold-m…
- 譯者:lsvih
- 校對者:HuskyDoge, atuooo
文件越小,意味着下載速度就越快。所以在向客戶端發送資源文件前,使文件變得更小是件有益的事情。html
其實,精簡與壓縮資源文件不只是一件很棒的事情,同時也是每一位現代開發者應該儘可能去作的事情。可是,用於精簡的工具一般沒法作到完美精簡;用於壓縮的壓縮器效果好壞會取決於用於壓縮的數據。下面介紹一些小技巧與方法,用於調整這些工具,使其達到最好的工做狀態。前端
咱們將以一個簡單的 SVG 文件爲例:android
這個<svg>
圖像的內容爲一個 10x10 像素的區域(viewBox
),其中包含了兩個 6x6 的正方形(<rect>
)。原始文件大小爲 176 字節,通過 gzip 壓縮事後大小爲 138 字節。ios
固然這個圖像並無什麼藝術感,但它足以知足這篇文章想要表達的意思,而且防止這篇文章變成長篇大論。git
運行 svgo image.svg
直接進行壓縮。github
(爲了便於閱讀,爲其添加了回車與縮進)算法
能夠明顯地看到,rect
被替換成了 path
。path
路徑形狀由它的 d
屬性定義,後面的一串命令相似於 canvas 的 draw 函數,控制一支虛擬的筆移動進行繪畫。命令能夠是絕對位移(移動到 x,y),也能夠是相對位移(向某方向移動 x,y)。請仔細觀察其中的一條路徑:canvas
M 0 0
:路徑起點爲座標(0, 0)
h 6
:水平向右移動 6 px v 6
:垂直向下移動 6 px H 0
:水平移動至 x = 0
z
:閉合路徑 — 移回路徑的起點後端
這個路徑畫出的正方形是多麼的精確!並且它比 rect
元素更加的緊湊。瀏覽器
另外,#f00
被改爲了 red
,這兒也少了一個字節!
如今文件大小爲 135 字節,gzip 壓縮事後爲 126 字節。
你可能已經注意到了,兩個路徑中的全部座標均爲偶數。咱們是否能夠把它們都除以 2 呢?
圖像和以前看起來是同樣的,但它縮小了兩倍。所以,咱們能夠對 viewBox
進行縮放,使圖像與以前同樣大。
如今文件大小爲 133 字節,gzip 壓縮事後爲 124 字節。
回過頭來看路徑。兩個路徑中的最後一個命令都是 z
,也就是「閉合路徑」。但路徑在填充的時候會被隱式地閉合,所以咱們能夠刪除這些命令。
又少了 2 字節,如今文件大小爲 131 字節,gzip 壓縮事後爲 122 字節。從常識上說,原始字節數越少,能壓縮的大小也越小。而如今咱們已經在 svgo 以後節省了 4 個 gzip 字節了。
你可能會想:爲何 svgo 不自動進行這些優化呢?緣由是縮放圖像與刪除尾部的 z 命令是不安全的。請看下面的例子:
這是一些有 stroke(路徑寬度)的圖形。從左至右分別爲:原始圖形、不閉合的狀況、不閉合且進行縮放的狀況。
線寬徹底混亂了。慶幸的是,咱們知道本身不須要使用線寬。可是 Svgo 並不知道這個狀況,所以它必需要保證圖形的安全,避免不安全的變換。
如今看起來不能從代碼中刪除任何東西了。XML 語法是嚴格的,如今全部的屬性都是必須的,而且它們的值不能不加引號。
你覺得結束了?並不,這僅僅是個開始。
如今,讓我來介紹一個很是方便的工具:gzthermal。它能夠分析須要進行 gzip 壓縮的文件,並對進行編碼的原始字節進行着色。更好壓縮的字節是綠色,很差壓縮的數據是紅色,簡單明瞭。
請再次關注 d
屬性,尤爲是被標成紅色的 M 命令值得注意。咱們不能刪除它,但咱們能夠用相對位移 m2 2
來代替它。
初始的「指針」位置爲座標軸原點(0, 0)
,所以移動到(2, 2)
和從原點移動(2, 2)
是同一個意思。讓咱們試試:
原始文件依然是 131 字節,可是通過 gzip 壓縮事後大小僅有 121 字節了。發生了什麼?答案是……
Gzip 使用的是 DEFLATE 壓縮算法,而 DEFLATE 算法是以哈夫曼樹爲基礎構建的。
哈夫曼編碼的核心思想就是使用更少的比特對出現次數更多的符號進行編碼,反之亦然,出現次數不多的符號須要佔用更多的比特。
沒錯,這兒說的是比特不是字節。DEFATE 算法會將一字節的字符視爲一系列的比特,不管一字節包含 七、九、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 友好的代碼時,應該更多地使用那些使用頻率較高的字符。即便你不能將代碼長度減短,但它通過壓縮後消耗的比特數也會變少。
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 有了更加清晰的認識。所以,咱們能夠將絕對位移命令 H0
和 H2
都替換爲相對位移命令:h-3
。
如今,兩個分開的回退引用合爲了一個,文件大小爲 133 字節,gzip 後的大小爲 119 字節。雖然咱們在壓縮前增長了 2 個字節,但 gzip 的結果又減小了 2 個字節!
咱們只須要關心壓縮後的大小便可:在傳送資源時,客戶端 99.9% 用的是 gzip 或者 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 的情景。
如下是一些能夠幫助你寫出更具有壓縮性能的代碼的準則:
你能夠在 此 GitHub repo 中找到以上全部資源、壓縮過的圖片以及其它資料。
但願你喜歡這篇文章。下次咱們將討論如何壓縮普通 JavaScript 代碼與 Webpack bundle 中的 JavaScript 代碼。
掘金翻譯計劃 是一個翻譯優質互聯網技術文章的社區,文章來源爲 掘金 上的英文分享文章。內容覆蓋 Android、iOS、前端、後端、區塊鏈、產品、設計、人工智能等領域,想要查看更多優質譯文請持續關注 掘金翻譯計劃、官方微博、知乎專欄。