WebP 图片实践之路

咱们会从三部分来聊聊webp这个话题。css

  1. 什么是webp,它有什么用?
  2. 使用webp的常规方法以及优劣。
  3. 咱们是如何用上webp的。

PS:若是是对webp有必定了解的朋友,建议直接看第三部分。由于是讲咱们的实践之路,因此第三部分会多讲一些。html

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

webp是谷歌推出的一种图片格式,它的优势就是同等画面质量下,体积比jpg、png这些少了25%以上。前端

你们都知道移动互联网时代,页面大小和用户留存息息相关,更快的加载页面才能让更多用户关注到你的内容,而图片一直都是页面体积的大头,拿咱们的活动页面来讲,图片占据了80%以上的页面大小。因此使用webp的话,能够瞬间让页面大小降低1/4,不得不说是一个极具性价比的优化点。node

固然,它也不是没有缺点,浏览器对于webp的解码速度相对于jpg来讲会慢一些,不过这和体积减少带来的性能提高,能够忽略不计了。webpack

那么既然webp这么好,为何没有大范围使用呢?归根结底仍是webp是谷歌推出的,目前主流浏览器只有chrome和安卓支持。不过IOS也快支持了,期待ing^ ^。在caniuse上能够查到webp目前的兼容性。web

563928-20160824222224026-513072318

2、使用webp的常规方法以及它们的优劣

首先,咱们须要一个工具把图片转成webp格式,这里就使用google的官方工具便可,连接chrome

这个装好以后,你的控制台就有了一个cwebp命令。运行cwebp -h,成功显示帮助信息就表示安装好了。shell

563928-20160824222352167-2066389043

经过这个工具就能够生成webp图片了,有了webp图片以后,以后即是如何使用了,常见有两种方案。npm

方案一:服务器端处理windows

这是最最最省心的方法了,支持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图片。

相关文章
相关标签/搜索