webp圖片實踐之路

最近,咱們在項目中實踐了webp圖片,而且抽離出了工具模塊,整合到了項目的基礎模板中。傳聞IOS10也將要支持webp,那麼使用webp帶來的性能提高將更加明顯。估計在不久的未來,webp會成爲標配。css

本文主要分享一下咱們在webp圖片使用上的實踐之路。html

 

咱們會從三部分來聊聊webp這個話題。前端

  1. 什麼是webp,它有什麼用?
  2. 使用webp的常規方法以及優劣。
  3. 咱們是如何用上webp的。

PS:若是是對webp有必定了解的朋友,建議直接看第三部分。由於是講咱們的實踐之路,因此第三部分會多講一些。node

 

1、什麼是webp,它有什麼用?

webp是谷歌推出的一種圖片格式,它的優勢就是同等畫面質量下,體積比jpg、png這些少了25%以上。webpack

你們都知道移動互聯網時代,頁面大小和用戶留存息息相關,更快的加載頁面才能讓更多用戶關注到你的內容,而圖片一直都是頁面體積的大頭,拿咱們的活動頁面來講,圖片佔據了80%以上的頁面大小。因此使用webp的話,能夠瞬間讓頁面大小降低1/4,不得不說是一個極具性價比的優化點。git

固然,它也不是沒有缺點,瀏覽器對於webp的解碼速度相對於jpg來講會慢一些,不過這和體積減少帶來的性能提高,能夠忽略不計了。github

那麼既然webp這麼好,爲何沒有大範圍使用呢?歸根結底仍是webp是谷歌推出的,目前主流瀏覽器只有chrome和安卓支持。不過IOS也快支持了,期待ing^ ^。在caniuse上能夠查到webp目前的兼容性。web

 

 

2、使用webp的常規方法以及它們的優劣

首先,咱們須要一個工具把圖片轉成webp格式,這裏就使用google的官方工具便可,連接
chrome

這個裝好以後,你的控制檯就有了一個cwebp命令。運行cwebp -h,成功顯示幫助信息就表示安裝好了。shell

經過這個工具就能夠生成webp圖片了,有了webp圖片以後,以後即是如何使用了,常見有兩種方案。

 

方案一:服務器端處理

這是最最最省心的方法了,支持webp圖片的瀏覽器在向服務器發送請求時,會在請求頭Accept中帶上image/webp。而後服務器就能夠根據是否含有這個頭信息來決定是否返回webp圖片了。

這個方法只須要在web服務器那裏作一些操做便可,十分簡單方便。

不過這個方案缺點也很明顯,首先經過請求頭檢測,某些設備可能不太準。其次,如今圖片等靜態資源都會放到CDN服務器上,那麼在這個層面加上判斷webp的邏輯就有點麻煩了。

 

方案二:前端檢測是否支持webp而後再請求相應格式的圖片

這個方法好處是十分穩妥,經過特性檢查能夠知道用戶的瀏覽器是否支持webp,壞處就是須要在業務代碼中加入檢測webp的邏輯。

一般作法是在頁面加載前先執行一段webp的檢測,得出瀏覽器是否支持webp格式,把結果存入cookie中,在加載圖片時,若是是懶加載的圖片,那麼根據是否支持webp來處理圖片路徑就好,若是不是懶加載的圖片,能夠在後端渲染模板時,根據咱們設置好的是否支持webp的cookie來判斷。

目前這些都是針對頁面經過img標籤引入圖片時兼容webp的方式。若是是css中引入的圖片,方案通常就是構建兩套css,而後在後端模板中根據cookie判斷使用哪一套,或是在css中經過選擇器覆蓋,好比對於支持webp的瀏覽器,咱們在html根節點上加上webps的類名,而後針對引入的圖片,經過這個類名作選擇器優先級覆蓋,具體的咱們在第三部分看着代碼細說。

 

 

3、咱們是如何用上webp的

重點來了,下面來講說咱們對webp的實戰。

首先說說咱們這邊現狀吧,咱們的圖片有兩種存放方式。對於一些動態圖片,好比商品圖,這些是存放在咱們的圖片服務器上,這個服務器支持webp格式,只須要在圖片路徑後面加上參數t=5便可獲得webp格式的圖片。

對於css引入的背景圖,咱們存放在某個CDN上,這部分就麻煩了,不支持生成webp圖片,因此只能本身傳一份相應的webp圖片上去。

並且因爲各類緣由和限制,咱們沒法採用上述說的服務器端處理方案,因此只能採用前端代碼處理的方式。我想有些公司沒使用webp可能也是這些緣由,由於純前端處理確實挺繞的。

 

結合咱們的業務狀況,由於是運營活動頁,背景圖和商品圖基本各佔一半,甚至背景圖更多,因此咱們須要把css引入的圖片和img標籤引入的圖片都作webp兼容T T。

針對img標籤引入的圖片,因爲咱們的圖片服務器支持webp,並且咱們的商品圖大可能是懶加載,那麼就簡單了,直接修改咱們的懶加載插件就能夠實現,在替換真實圖片路徑的時候判斷一下是否支持webp,而後替換相應的路徑就能夠。

 

針對css引入的圖片,咱們採起的方案是利用css的優先級覆蓋,好比說若是瀏覽器支持webp,那麼咱們給html根節點上加上webps的類名。這樣好比咱們寫

span{background-image:url(a.jpg)}

的時候,再寫上

.webps span{background-image:url(a.jpg.webp)}

這樣,支持webp格式的設備就會自動加載webp的圖片了。

固然這裏你確定會有兩個疑問

一是每次寫代碼的時候加上.webps再寫一遍工做量也太大了。

二是每張圖對應的webp圖片是哪裏來的?須要本身生成嗎?

針對這兩個問題,咱們找到了相應的解決方法,對於問題一咱們使用css預處理器作到了生成對應的webp的代碼。

問題二咱們使用nodejs寫了一個腳原本監控圖片文件夾,當圖片增長、修改、刪除時,它便會生成或刪除對應的webp圖片。

 

說了這麼多,咱們一塊兒來看一看代碼實現吧。

首先,咱們須要在頁面最開始的部分加入一段webp的檢查代碼。這段代碼的做用就是檢查當前瀏覽器是否支持webp,若是支持,那麼給html根節點加上webps的類名,以供css使用。而且在cookie中記錄一個名爲webps,值爲A的cookie,爲期一年。這樣,以後就能夠在css中使用webp類名作兼容處理,img標籤引入的圖片也能夠經過cookie得知瀏覽器是否支持webp,而後作相應處理,後端也能夠經過cookie得知設備對webp的支持狀況來作一些差異渲染。

這段代碼以下,須要注意的是這段代碼要在引入css前就加載,代碼的含義能夠直接看註釋。

;(function(doc) {

    // 給html根節點加上webps類名
    function addRootTag() {
        doc.documentElement.className += " webps";
    }

    // 判斷是否有webps=A這個cookie
    if (!/(^|;\s?)webps=A/.test(document.cookie)) {
        var image = new Image();

        // 圖片加載完成時候的操做
        image.onload = function() {

            // 圖片加載成功且寬度爲1,那麼就表明支持webp了,由於這張base64圖是webp格式。若是不支持會觸發image.error方法
            if (image.width == 1) {

                // html根節點添加class,而且埋入cookie
                addRootTag();
                document.cookie = "webps=A; max-age=31536000; domain=58.com";
            }
        };

        // 一張支持alpha透明度的webp的圖片,使用base64編碼
        image.src = 'data:image/webp;base64,UklGRkoAAABXRUJQVlA4WAoAAAAQAAAAAAAAAAAAQUxQSAwAAAARBxAR/Q9ERP8DAABWUDggGAAAABQBAJ0BKgEAAQAAAP4AAA3AAP7mtQAAAA==';
    } else {
        addRootTag();
    }
}(document));

 

而後咱們處理img標籤引入的圖片,由於咱們的圖片服務器支持webp,且用img引入的圖片都是經過懶加載來載入的,因此這部分咱們處理起來比較簡單,在懶加載替換真實路徑的時候,判斷cookie中是否存在webps=A這個cookie來決定加載的圖片的url。

固然,若是大家不是懶加載的引入的圖片,那麼能夠在後端渲染的時候,經過咱們寫入的cookie,來判斷是否使用webp圖片,也很方便。這部分代碼比較簡單,就不貼出來了。

 

而後是css中引入的圖片了,因爲css不支持邏輯,咱們如今能利用的就是html根節點的.webps的類名了。咱們在SCSS中使用了這個mixin來加載圖片。代碼做用能夠看下注釋。

/*
    經過這個函數來引入圖片,例如:
    #wrapper{ @include bg('../img/sample.jpg') }
    這段代碼通過編譯後便會生成以下兩句代碼
    #wrapper{ background-image:url('../img/sample.jpg'); }
    .webp #wrapper{ background-image:url('../img/sample.jpg.webp'); }
 */
@mixin bg($url) {
    background-image: url($url);
    @at-root(with: all) .webps & {
        background-image: url($url + '.webp');
    }
}

 

若是用的是less,能夠經過下面這段代碼來實現一樣的功能。

.mixin(@url) {
    background-image: url(@url);
    .webps & {
        background-image: url('@{url}.webp');
    }
}

 

最後就是如何生成webp圖片了。對於css引入的圖片,因爲是放在CDN上,咱們須要本身生成對應的webp圖片。

 

如何作到開發的時候自動配套生成webp圖片呢,開始咱們想的是給咱們的構建工具寫個插件來實現編譯時候生成webp圖片,然而咱們發現因爲各個項目使用的構建工具可能不同,好比fis三、webpack還有咱們本身開發構建工具的,太多了,針對每個開發成本過高。因此咱們決定用nodejs寫個小腳本,做用就是監控咱們的圖片文件夾,隨時生成配套的webp圖片,當圖片有增長、修改、刪除時,它會相應的增長、修改、刪除對應的webp圖片。

工具代碼以下。默認監聽images文件夾,npm install 安裝依賴以後,直接node webp-monitor.js既可。固然,前提是你按照好了第二部分所說的谷歌官方的webp生成工具,或者簡單的說你的終端須要有cwebp這個命令才行。

 

/*
    webp圖片生成
    
    運行:npm install && npm start

    程序依賴谷歌官方webp轉換工具cwebp
    mac下安裝 brew install webp
    windows下能夠去google官方下載

    安裝完成後運行cwebp -h 若是顯示了使用幫助則表示安裝成功
 */

const process = require('child_process');
const fs = require('fs');
const chokidar = require('chokidar');

const log = console.log.bind(console);
const ignoreFiles = /(^\..+)|(.+[\/\\]\..+)|(.+?\.webp$)/; // 忽略文件.開頭和.webp結尾的

let quality = 75; // webp圖片質量,默認75
let imgDir = 'images'; // 默認圖片文件夾

// 獲得對應的webp格式的文件名,默認爲文件名後加上.webp
function getWebpImgName(path) {
    return `${path}.webp`;
}

// 獲得shell命令
function getShellCmd(path) {
    return `cwebp -q ${quality} ${path} -o ${getWebpImgName(path)}`;
}

// 監控文件夾
var watcher = chokidar.watch(imgDir, {
    ignored: path => {
        return ignoreFiles.test(path);
    },
    persistent: true // 保持監聽狀態
});

// 監聽增長,修改,刪除文件的事件
watcher.on('all', (event, path) => {
    switch (event) {
        case 'add':
        case 'change':
            generateWebpImg(path, (status) => {
                log('生成圖片' + getWebpImgName(path) + status);
            });
            break;
        case 'unlink':
            deleteWebpImg(getWebpImgName(path), (status) => {
                log('刪除圖片' + getWebpImgName(path) + status);
            });
            break;
        default:
            break;
    }
});

log('biubiubiu~~~ 監控已經啓動');

function generateWebpImg(path, cb) {
    process.exec(getShellCmd(path), err => {
        if (err !== null) {
            cb('失敗');
            log('請先運行cwebp -h命令檢查cwebp是否安裝ok。')
            log(err);
        } else {
            cb('成功');
        }
    });
}

function deleteWebpImg(path, cb) {
    fs.unlink(path, (err) => {
        if (err) {
            cb('失敗');
            log(err)
        } else {
            cb('成功');
        };
    });
}

 

至此,咱們便實現了在項目中使用webp圖片。咱們首先拿了一個活動頁試水,應該是明天會上線,上線後我把地址貼出來,http://m.zhuanzhuan.58.com/Mzhuanzhuan/zhuanzhuan/zzactivity/activity-xbl/你們能夠看看效果如何,經測試圖片確實小了不少呢。

 

文章中聊到的相關代碼都放到的github上,地址以下https://github.com/huangjiaxing/webp-monitor

 

感受是一個比較民工的webp實踐方案,對於想使用webp可是卻不想和運維那些打交道的,能夠嘗試下這個方案,仍是挺不錯的^ ^。

 

 

 

轉載本站文章請註明做者和出處 哎呦大黃 – http://www.cnblogs.com/season-huang/ ,請勿用於任何商業用途

相關文章
相關標籤/搜索