在 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
相關文章
相關標籤/搜索