在 CRA 中使用 webp 图片提高加载性能

webp 是 google 提倡的一种新的 image 格式,意在为 web 提供体积更小的图片格式。一般状况下,无损压缩能够减少 25%-35% 的体积(有例外状况,反而会增大致积,可是是由于转换图片格式不兼容引发的),有损压缩最大能够节省大约 75%-90% 的体积。javascript

兼容性

使用新的浏览器特性,首先应该考虑兼容性问题,它的兼容性以下图:html

image.png

能够发现,除了 ie 和 safari 以外,基本都支持了该格式,并且 safari 14 也即将支持该格式,到目前为止,全球浏览器的 ~75.9%(粗略统计) 份额的浏览器都可使用该功能。java

如何断定兼容性

https://github.com/DonRai/react-image-webp/blob/master/modules/utils/index.js

核心代码以下:react

const el = document.createElement('canvas') el.toDataURL('image/webp').indexOf('data:image/webp') === 0;

若是浏览器支持 ​webp​ 这种 ​mime-type​ 的话,则输入的 ​base64 字符串会包含特定的关键字(这种手段也能够用来检测浏览器是否支持别的格式)。webpack

js 解决方案

因为能够经过 js 来断定浏览器是否支持该特性,因此问题也很好解决,只须要作一个逻辑断定便可,好比:git

{  isWebpSupported() ? 
    <img src={require('./path/to/img.webp')} /> : 
    <img src={require('./path/to/img.png')} /> }

html 解决方案

另外一种解决方案是,咱们把图片的选择逻辑,委托给浏览器,刚好 html 规范中,有一个 ​picture 标签,这个标签配合 ​source 和 ​img 标签,能够完美地解决这个问题,以下:github

<picture>  
    <source srcset="logo.webp" type="image/webp">
    <img src="logo.png" alt="logo"> 
</picture>

浏览器当遇到这段代码时,会自动匹配 ​source 中的备选多媒体资源,尽量地使用最恰当的那一个资源。web

这里可能有一个问题,就是 ​picture 标签的兼容性问题,以下:canvas

image.png

能够发现除了 ieopera mini 均支持,因为 ie 自己也不支持 ​webp 格式,因此咱们能够忽略它。浏览器

在 create-react-app 中使用它

CRA 自己已经支持 ​webp 格式的图片,可是图片须要是静态的,即你首先应该有一个 ​webp 图片,若是是对于将来的图片,那没什么问题,但对于以前已经使用的图片,就必须手动一张一张转换,有点繁琐,有没有解决方案可以自动将以前的 ​jpg 或者 ​png 的图片转换为 ​webp 格式,或者在打包时,同时生成一个 ​webp 格式的副本呢? 答案是有的,可使用 ​ImageminWebpWebpackPlugin 这个插件来完成这个工做,以下:

new ImageminWebpWebpackPlugin({
      config: [
        {
          test: /\.(jpe?g|png)/,
          options: {
            quality: 75,
          },
        },
      ],
      overrideExtension: true,
      detailedLogs: false,
      silent: false,
      strict: true,
    })

在 CRA 中,能够经过 ​eject 或者 ​react-app-rewired 来覆盖 webpack 配置,我这里使用的是 customize-cra 这个库中的 addWebpackPlugin 方法。

该插件的默认的生成规则是,xxx.png 在打包时,同时会生成一个 ​xxx.webp 的副本,固然这个规则也能够在插件的配置中进行更改。

最后只须要把 img 元素简易封装一下便可,以下:

const WebpImage: React.FC<
  React.DetailedHTMLProps<
    React.ImgHTMLAttributes<HTMLImageElement>,
    HTMLImageElement
  >
> = props => {
  const { src } = props;

  const webpSrc = React.useMemo(() => {
    const nameChunks = src.split('.');
    nameChunks.pop();
    nameChunks.push('webp');
    return nameChunks.join('.');
  }, [src]);

  return (
    <picture>
      <source srcSet={webpSrc} type="image/webp" />
      <img {...props} />
    </picture>
  );
};

这里的封装比较简单,但做为演示够用了,效果以下:

image.png

network 中的加载状况:

image.png

总结

我示例中的图片,源文件大小为 184kbwebp 副本文件大小为 22kb,以下图:
image.png

因为我这里是有损压缩,因此体积减小比例大概是 ~88%,无损压缩的话,会比这个低一些。

参考

https://developers.google.com/speed/webp/
https://github.com/DonRai/react-image-webp
https://developer.mozilla.org/en-US/docs/Web/HTML/Element/picture
相关文章
相关标签/搜索