webp圖片實踐之路

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

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

我們會從三部分來聊聊webp這個話題。

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

PS:如果是對webp有一定了解的朋友,建議直接看第三部分。因為是講我們的實踐之路,所以第三部分會多講一些。


一、什么是webp,它有什么用?


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

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

當然,它也不是沒有缺點,瀏覽器對于webp的解碼速度相對于jpg來說會慢一些,不過這和體積減小帶來的性能提升,可以忽略不計了

那么既然webp這么好,為什么沒有大范圍使用呢?歸根結底還是webp是谷歌推出的,目前主流瀏覽器只有chrome和安卓支持。不過IOS也快支持了,期待ing^ ^。在caniuse上可以查到webp目前的兼容性。


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


首先,我們需要一個工具把圖片轉成webp格式,這里就使用google的官方工具即可,鏈接。

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

563928-20160824222352167-2066389043.png
563928-20160824222352167-2066389043.png

通過這個工具就可以生成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的類名,然后針對引入的圖片,通過這個類名做選擇器優先級覆蓋,具體的我們在第三部分看著代碼細說。


三、我們是如何用上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圖片,然而我們發現由于各個項目使用的構建工具可能不一樣,比如fis3、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/


PS:

1.《Firefox將支持谷歌超微型WebP圖像格式》

2.《Google想通過WebP統一互聯網圖片格式》

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,182評論 6 543
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,489評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,290評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,776評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,510評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,866評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,860評論 3 447
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,036評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,585評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,331評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,536評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,058評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,754評論 3 349
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,154評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,469評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,273評論 3 399
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,505評論 2 379

推薦閱讀更多精彩內容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,734評論 25 708
  • 發現 關注 消息 iOS 第三方庫、插件、知名博客總結 作者大灰狼的小綿羊哥哥關注 2017.06.26 09:4...
    肇東周閱讀 12,173評論 4 61
  • 問答題47 /72 常見瀏覽器兼容性問題與解決方案? 參考答案 (1)瀏覽器兼容問題一:不同瀏覽器的標簽默認的外補...
    _Yfling閱讀 13,781評論 1 92
  • 村口的風好大 呼呼地吹在臉上 我卻感覺不到寒冷 太陽又開始落山了 媽媽打電話說 這幾天要回來看我 爸爸也回來 奶奶...
    六月天氣閱讀 309評論 18 36
  • 舞,誘惑著原始 文·三峽劉星 這是一個小型的舞會,沒有太絢麗的舞臺,也沒有華麗至極的音箱,但是,這適合你,你的長發...
    三峽劉星閱讀 545評論 1 1