webp是该用起来了

技术
Back

webp 的好处不用多说了,相比 jpg 和 png 格式,文件大小能节省很多。但因为并不是所有浏览器都支持,所以需要做一些兼容处理。

得到 webp

随便搜索引擎一搜,会有很多在线转换图片格式的工具,也有很多可供下载的工具,看你自己怎么用了,比如说前端的gulp,会有对应的图片压缩转换的插件可使用。这里我就说下 google 提供的的命令行工具webp

在 Mac 下可以使用 homebrew 安装 webp 工具:

brew install webp

安装完成之后,就可以使用 cwebp 将 jpg 或 png 转成 webp 格式:

cwebp [-preset <...>] [options] in_file [-o out_file]

更多的操作自行查阅文档吧。

后端处理

这里就不细说了,大概原理就是在浏览器向服务器发起请求时,对于支持 webp 图片的浏览器,会在请求头 Accept 中带上 image/webp 的信息,服务器就能识别浏览器是否支持 webp 从而返回对应的图片格式,具体可查看凹凸实验室的文章——探究 WebP 一些事儿

前端处理

在页面上可以使用picture标签来做兼容,会根据浏览器设备版本选择不同的选项:

<picture>
  <!-- JPEG -->
  <source srcset="./images/example.jpg" type="image/jpeg" />
  <!-- WebP -->
  <source srcset="./images/example.webp" type="image/webp" />
  <!-- The fallback -->
  <img src="./images/example.jpg" alt="example" />
</picture>

但这样写会比较臃肿,可以通过Service Wroker来拦截网络请求,监听 fetch 事件,根据 Accept 头部来判断浏览器是否支持 webp 图片,并且查找是否存在image/webpMime 类型,这样就可以确定并替换成 webp 返回。

// serviceWorker.ts
window.addEventListener('fetch', (e: any) => {
  // Clone the request
  const req = e.request.clone()

  // Check if the image is a jpeg
  if (/\.jpg$|.png$/.test(e.request.url)) {
    // Get all of the headers
    const headers: string[] = Array.from(req.headers.entries())

    // Inspect the accept header for WebP support
    const acceptHeader: string[] = headers.filter(
      (item) => item[0] === 'accept'
    )
    const supportsWebp = acceptHeader[1].includes('webp')

    // If we support WebP
    if (supportsWebp) {
      // Build the return URL
      const returnUrl = req.url.substr(0, req.url.lastIndexOf('.')) + '.webp'

      e.respondWith(
        fetch(returnUrl, {
          mode: 'no-cors'
        })
      )
    }
  }
})

然后页面就可以写得简单些:

<picture>
  <source srcset="./images/example.jpg" type="image/jpeg" />
</picture>

但看了下这个例子,也一样会去请求 jpg 的图片,那这样就不起作用了。

React 下使用且要支持 CSS 内使用

上面的方式是挺方便的,但因为我的个人网站目前使用 react,有些图片是导入进去使用的,我试了下如此 service worker 的方式就不支持了。加之很多时候会在 css 内引入背景图片,所以不能在 service worker 做判断,那就还是在页面上操作:

const ImgWithFallback = ({
  src,
  fallback,
  type = 'image/webp',
  ...delegated
}) => {
  return (
    <picture>
      <source srcSet={src} type={type} />
      <img src={fallback} {...delegated} />
    </picture>
  )
}

使用的时候是:

<ImgWithFallback
  src={require('./images/example.webp')}
  fallback={require('./images/example.png')}
  alt="example"
/>

想要在 css 内使用 webp,就得知道浏览器是否支持 webp,然后在 html 加上一个 class 做标识,如此在 css 内使用 background 引入图片的时候,就可以根据 class 去引入不同格式的图片。

如何知道浏览器是否支持 webp 呢,创建一个 canvas 判断下:

function canUseWebP() {
  var elem = document.createElement('canvas')
  if (!!(elem.getContext && elem.getContext('2d'))) {
    // was able or not to get WebP representation
    return elem.toDataURL('image/webp').indexOf('data:image/webp') == 0
  }
  // very old browser like IE 8, canvas not supported
  return false
}

然后根据判断在 html 标签加上 class 即可。

参考:

© RAYLIAORSS