圖片壓縮、加載和格式選擇

本文首發於政採雲前端團隊博客:性能優化——圖片壓縮、加載和格式選擇

https://www.zoo.team/article/images-compress

前言

相信大家都聽說過 "258 原則 (https://blog.csdn.net/weixin_42139375/article/details/83001248)" ,一個網站的性能好壞很大程度上會影響到用戶的體驗。

在我經歷的多個電商與大屏項目的優化性能的項目後,我發現圖片資源的處理在網站性能優化中有着舉足輕重的作用。

一般電商網站請求數據

在首屏加載的 145 個請求中圖片資源請求佔到了 75% 以上,在所有請求靜態資源中圖片也佔有着很大的比重。可見圖片優化的重要性。

不過在認識圖片優化前我們先了解下二進制位數色彩呈現的關係。

二進制位數與色彩

在計算機中,一般用二進制數來表示像素。在不同的圖片格式中,像素與二進制位數之間對應的關係是不同的。一個像素對應的二進制位數越多,它能表示的顏色種類就豐富,成像效果也就越精緻,圖片所需的存儲空間相應也會越大。

目前市場上優化圖片資源的方式有很多,如壓縮圖片、選擇正確格式、 CDN 加速、懶加載等。

壓縮圖片

壓縮圖片相信是大家第一時間想到的方案。像我們比較熟悉的 tinpng (https://tinypng.com/),他的原理是通過有 "選擇性" 地減少圖像所要存儲的顏色數量,來減少圖片所要存儲的內存。

When you upload a PNG (Portable Network Graphics) file, similar colors in your image are combined. This technique is called “quantization”. By reducing the number of colors, 24-bit PNG files can be converted to much smaller 8-bit indexed color images.

下面我們來看下樣例:

細節展示:

圖片格式

壓縮圖片雖然在一定程度上可以減少我們請求的資源所需要的帶寬,但如果是用對了格式在性能上往往會有質的改變。

JPEG / JPG

JPEG 是最常用的圖像文件格式。

優勢

缺陷

JPG 的有損壓縮在輪播圖背景圖的展示上確實很難看出破綻,但當它處理矢量圖形和 Logo 等線條感較強、顏色對比強烈的圖像時,人爲壓縮導致的圖片模糊會相當明顯。因此不適宜用該格式來顯示高清晰度線條感較強的圖像。

除此之外, JPG 並不支持對有透明度要求的圖像進行顯示,如果需要顯示透明圖片還是需要另尋它路。

業務場景

JPG 適用於呈現色彩豐富的圖片,在我們日常開發中,JPG 圖片經常作爲大的背景圖輪播圖預覽圖出現。打開某電商網站首頁,即可看到大圖片的處理幾乎都是使用了  JPG

PNG - 8 與 PNG - 24

png 是一種採用無損壓縮算法的位圖格式。

優勢

缺點

體積太大

業務場景

理論上來說,當你追求最佳的顯示效果 (詳情展示圖、圖片有放大需求、攝影作品等),並且不在意存儲大小或所需帶寬時,可以使用 PNG-24 (https://baike.baidu.com/item/PNG/174154?fr=aladdin)。但實踐當中,爲了避免文件體積過大的問題,我們一般不用 PNG 去處理較複雜的圖像。當我們遇到適合 PNG 的場景時,也會優先選擇更爲小巧的 PNG-8

亦或者需要處理有透明度或線條明顯的圖片時,也會採用 PNG 。如網站主 logo:

SVG

嚴格來說應該是一種開放標準的矢量圖形語言。

優點

缺點

業務場景

SVG 是文本文件,我們既可以像寫代碼一樣定義 SVG ,把它寫在 HTML  裏、成爲 DOM 的一部分。用的比較多的就是 iconfont (https://www.iconfont.cn/)。我們可以通過設置模塊的 fill 屬性輕鬆適配圖標的換膚功能,並通過 font-size 調節其大小。

Base64

一種基於 64 個可打印字符來表示二進制數據的方法。

優點

缺點

業務場景

Base64雪碧圖一樣,是作爲小圖標解決方案而存在的。

Base64 是一種用於傳輸 8Bit 字節碼的編碼方式,通過對圖片進行 Base64 編碼,我們可以直接將編碼結果寫入 HTML 或者寫入 CSS ,從而減少 HTTP 請求的次數。

Elements 中搜索 “base64” 關鍵字,你會發現 Base64 也有很多使用的地方。而且它對應的圖片佔用內存較小。

既然 Base64 這麼棒,我們把所有圖片都用Base64 好了嘛。

Base64 編碼後,圖片大小會膨脹爲原文件的 4/3( Base64 編碼原理 (https://blog.csdn.net/wo541075754/article/details/81734770))。如果我們把大圖也編碼到 HTML 或 CSS 文件中,後者的體積會明顯增加,即便我們減少了 HTTP 請求,也無法彌補這龐大的體積帶來的性能開銷。也就是說我們犧牲的渲染性能大於資源請求性能,這樣做不太值得。

我們可以看到,大多數用 Base64 編碼的圖片都是小圖。

WebP

一種同時提供了有損壓縮與無損壓縮(可逆壓縮)的圖片文件格式。

優點

缺點

業務場景

JPEG/JPG 。因爲目前兼容性不好,一般搭配 JPEG/JPG 一起使用。

圖片格式小結

給大家整理了思維導圖:

OSS 搭配 CDN


我們原始的方式是將圖片等資源一起放入項目中打包上線。

這樣做的缺點在於打包出來的包大不說,用戶請求資源的速度也會受到限制。比如我們的服務器在華南,華北的用戶請求就會稍慢。當遇到併發量大的情況時,從部署服務器請求接口與資源這無外乎給我們的服務器提供了多餘的壓力。當我們臨時想替換一張圖片時,也需要重新打包併發布上線,非常麻煩。

當我們將圖片進行 OSS 放置並 CDN 加速後,這個問題就得到了很好的解決。不同地區的用戶可以訪問就近服務器,重複的請求也會產生緩存,避免 OSS 流量的浪費。

《OSS 和 CDN 的區別》(https://www.cnblogs.com/jsfh/p/14076992.html) 大家也可以參考這篇文章進行細看。

圖片的懶加載

相信大家一定會遇到首屏數據過多加載緩慢的情況。在這個情況下我們就需要考慮懶加載了。當用戶滾動到預覽位置時,在進行圖片數據的請求。期間用骨架屏或縮略圖代替。

window.onload = function () {
    // 獲取圖片列表,即 img 標籤列表
    var imgs = document.querySelectorAll('img');
    // 獲取到瀏覽器頂部的距離
    function getTop(e) {
        return e.offsetTop;
    }
    // 懶加載實現
    function lazyload(imgs) {
        // 可視區域高度
        var h = window.innerHeight;
        // 滾動區域高度
        var s = document.documentElement.scrollTop || document.body.scrollTop;
        for (var i = 0; i < imgs.length; i++) {
            //圖片距離頂部的距離大於可視區域和滾動區域之和時懶加載
            if ((h + s) > getTop(imgs[i])) {
                // 真實情況是頁面開始有2秒空白,所以使用 setTimeout 定時 2s
                (function (i) {
                    setTimeout(function () {
                        // 不加立即執行函數i會等於9
                        // 隱形加載圖片或其他資源,
                        // 創建一個臨時圖片,這個圖片在內存中不會到頁面上去。實現隱形加載
                        var temp = new Image();
                        temp.src = imgs[i].getAttribute('data-src');//只會請求一次
                        // onload 判斷圖片加載完畢,真是圖片加載完畢,再賦值給 dom 節點
                        temp.onload = function () {
                            // 獲取自定義屬性 data-src,用真圖片替換假圖片
                            imgs[i].src = imgs[i].getAttribute('data-src')
                        }
                    }, 2000)
                })(i)
            }
        }
    }
    lazyload(imgs);
    // 滾屏函數
    window.onscroll = function () {
        lazyload(imgs);
    }
}

尾聲

性能優化是我們前端開發工程師必須要掌握的一門硬技能。和學習其他新技術不同的是,當你想學習一套新的框架時,閱讀文檔和源碼幾乎可以讓你在使用過程中游刃有餘。但性能優化卻不一樣,它只能讓我們去摸索去領悟去突破,它是一種經驗也是一種習慣更是一種嗅覺。

參考資料

最佳實踐:使用阿里雲 CDN 加速 OSS 訪問 (https://developer.aliyun.com/article/770616?utm_content=g_1000173381)

掘金小冊:  前端性能優化原理與實踐 (https://juejin.cn/book/6844733750048210957)

壁紙網站: wellhaven (https://wallhaven.cc/)

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/FtaUER4Hv3RzCReKTqIRNQ