如何利用Webp和http缓存节省30%的网络流量

  Webp推出那年,我刚刚考上高中。转眼间,大学毕业将近一年,我依旧是那个青葱少年!就像Webp同样,仍是那么年轻,时至今日还没有崭露头角,缘由是各大浏览器对它的兼容依旧不是那么的友好。IE爸爸甚至至今都没有要支持它的迹象。javascript

webp

维基百科:WebP最初在2010年发布,目标是减小文件大小,但达到和JPEG格式相同的图片品质,但愿可以减小图片档在网络上的发送时间。 2011年11月8日,Google开始让WebP支持无损压缩和透明色(alpha通道)的功能,而在2012年8月16日的引用实作libwebp 0.2.0中正式支持。根据Google较早的测试,WebP的无损压缩比网络上找到的PNG档少了45%的文件大小,即便这些PNG档在使用pngcrush和PNGOUT处理过,WebP仍是能够减小28%的文件大小。

  简单来讲,Webp格式是一种图片格式,它有更优秀的图片压缩算法,而且能实现肉眼难以辨识的质量差别,同时它还支持有损无损两种压缩模式。html

  因为是谷歌的亲儿子,因此安卓原生浏览器对Webp的支持仍是比较乐观的,Chrome桌面版和安卓版的支持也都比较好。国内的浏览器也不一样程度的对Webp作了不少支持。我去Can I Use上截了张图过来:java

  因而可知,市面上占有率比较大的浏览器对Webp的支持仍是很不错的,因此有必要使用起来。实际上是以前无心间发现某宝和某东在使用,因此也想在这块作一些优化。ios

  有图有真相,先看看优化后的效果吧。nginx

  使用前:

  使用后:

  能够很明显的看到,仅仅这八张并非特别大的图片,便节省了59.3K的流量。下载时间也有明显的缩短。若是你的web项目是相似于某宝某东那样有着大量图片,那么这块节省的流量可想而知!web

  市面上有一些图片格式转换工具,我这里就不一一列举了。这里要讲的多是比较简单的一种使用方式,由于咱们的图片等资源文件托管在阿里云oss上,它只须要你在请求url里面带个参数,就会自动返回你想要的图片格式。各位看官,若是大家的状况和我不同,可能须要本身对图片作一部分处理或者别的云存储也有相似的解决方案。算法

  好的,言归正传,接下来讲说个人解决方案。咱们的Web是利用Vue实现的先后端同构的,因此存在服务端渲染和客户端渲染两种状况,这就要求咱们要分别在服务端和客户端对浏览器是否支持Webp做出判断,若是浏览器支持,就去oss取webp格式的图片,不然继续使用本来图片格式。canvas

  我所采起的方法是在封装网络请求的时候,作了一步判断,而后把是否支持Webp的变量放到了环境变量中。因为网络请求须要同时支持客户端和服务端,因此我采用的是axios并本身作了一层封装。axios

//  封装axios
createRequest = (req) => {
    // 若是在客户端建立
    if (process.client) {
        process.env.supportWebp = document.createElement('canvas').toDataURL('image/webp').indexOf('data:image/webp') === 0
    } else {
     //  从服务端检测客户端是否支持webp
        if (req && req.headers) {
            process.env.supportWebp = req.headers.accept.indexOf('image/webp') > -1
        }else{
            process.env.supportWebp = false
        }
    }
}

  其实不管是客户端仍是服务端,均可以采用判断accept里面是否带有'image/webp'的方式,可是有些童鞋说判断accept方式有些浏览器不许确,因此咱们在客户端采用较为稳妥的方式去判断。后端

  每一个人的框架或者环境可能不一样,因此代码不必定能照搬,只需理解这部分的思想:根据不一样的环境判断浏览器是否支持Webp。

  在使用的时候,对于页面中的img,我写了一个过滤器:

<img :src="item.image_url | checkWebp">
export function judgeWebp (src) {
  if(process.env.supportWebp + '' === 'true'){
    return src + '?x-oss-process=image/format,webp'
  }
  return  src
}
const filters = {
  //......,
  judgeWebp
}
Object.keys(filters).forEach(key => {
  Vue.filter(key, filters[key])
})

  对于背景图片,style动态绑定彷佛是不能使用过滤器的,因此采用计算属性的方式实现。各位看官若是有更好的方法欢迎提出来。

  到此为止,咱们能够根据浏览器是否支持webp来获取到不一样格式的图片了。另外,有些社区也有过利用第三方polyfill来实现浏览器兼容Webp的方案。可是彷佛并非那么的流行,追求稳妥的状况下,我这里暂不使用,若是你有过相似的实践,欢迎与我分享。

http缓存

  webp的分享就到这里,接下来咱们简单聊聊http缓存。http缓存大体分为两类,一类是强制缓存,另外一类叫对比缓存。这两种缓存方式是能够同时存在的。强制缓存,一听这名字就威武霸气,因此它的优先级也是比较高的,就是说,若是强制缓存生效,对比缓存就再也不执行。另外一个区别点就是,强制缓存若是生效,就再也不和服务器交互了,对比缓存则须要每次都和服务器交互协商。

强制缓存

  先说强制缓存。浏览器向服务器请求数据,返回的header头中会携带缓存规则。体如今Expires和Cache-Control这两个属性当中。

  Expires是HTTP 1.0的东西,能够说是历史遗留产物了。它的值是到期时间,若是请求时间小于这个到期时间,就会采用缓存。咱们一眼就能发现这个逻辑其实意义并不大,并且若是服务端和客户端时间不一致,会有偏差产生。

  Cache-Control彷佛是为弥补Expires的天生缺陷而生的。它俩若是同时存在,Expires则不会生效。它的取值能够为:

取值 含义
private 可被缓存,但不能在用户之间共享
public 可被缓存,而且在多用户间共享
no-store 不缓存
no-cache 使用对比缓存与服务器交互
max-age=xxx 设定缓存有效期(单位秒)

  这里须要区别no-store和no-cache,谨记no-store是不作缓存,而no-cache是使用对比缓存。彷佛翻译过来很像,可是实际效果差不少,对于no-store这种不缓存,除非特殊状况,咱们通常不使用。

对比缓存

  咱们再聊聊对比缓存。对比缓存主要分两大块,一起是根据修改时间判断缓存是否生效,另外一块是经过Etag(我的理解就是个hash值)来判断。

Last-Modified && If-Modified-Since

  Last-Modified是存在于返回的header中的,顾名思义,它告诉咱们这个资源的最后修改时间。当浏览器再次发起请求的时候,会由If-Modified-Since带着这个值到服务器去作对比,若是服务器发现这个值小于目前服务器上资源的Last-Modified,则会把新的文件返回,状态码200。若是大于等于则只返回携带304状态码的请求,通知浏览器这个值还没有失效。

  它的缺点是这里的时间值只能精确到秒。

Etag && If-None-Match

  Etag能够理解为服务器给资源打的hash值,就相似于咱们使用构建工具打包资源文件后面会跟一条经常的字符串同样,它保证资源文件的惟一性。Etag随response返回给浏览器,同理浏览器下一次请求会由If-None-Match携带Etag的值去到服务器做比对。若是发现这个值不存在,说明本地的资源文件已经失效,服务器返回新的资源文件给客户端,状态码200。反之,返回304通知客户端资源文件仍然生效。

  相较于对比最后修改时间的策略,它的优势在于能够突破精确到秒的限制,另外若是咱们有一些按期更新的文件,可是资源内容不变,Etag的优点就更为明显了。

  这里要说起的一点是,当Last-Modified和Etag策略同时生效的时候,Etag的优先级更高。

结语

  原本是想记录一下webp的,可是既然是优化,就顺便写了写http缓存这块的内容。除此以外,各位还能够根据实际状况合理配置cdn以及nginx等的缓存,以实现更好的用户体验。优化路上无止境,希望咱们都能往极致的方向去作。今天就聊到这儿,有什么说错的地方还请各位看官批评指正,但愿你们多多指教!

相关文章
相关标签/搜索