BACK
Featured image of post Vue-cli plugin:使用套件將圖片自動轉檔為webp格式

Vue-cli plugin:使用套件將圖片自動轉檔為webp格式

webp 是 2010年 Google 釋出的圖片格式,針對 PNG 可減少 26%,JPEG 約可減少 25% ~ 34%,目前僅 safari、IE 尚不支援,但 safari 在 ios 14 以後開始支援。

參考網站
Vue CLI 官方webpack相關文檔


前言

  • webp 是 2010年 Google 釋出的圖片格式,針對 PNG 可減少 26%,JPEG 約可減少 25% ~ 34%,目前僅 safari、IE 尚不支援,但 safari 在 ios 14 以後開始支援。
  • 若在未提供 .webp 格式圖檔的情況下,使用套件於 webpack 時進行圖片轉檔,但於 development 下會因抓不到虛擬的 XXX.webp 圖檔而導致 npm run 起時噴錯,可使用以下設定解決此問題。

使用 webpack-plugin-image-transform-webp-and-mini 套件將 image 轉檔成 webp 格式

1
npm i webpack-plugin-image-transform-webp-and-mini

新增一個自訂的 webploader

  • loader 功用為:若 resource 的 query (使用 chainWebpack 提供的變數"resourceQuery“抓) 字符中含有 "type=webp"resource 為圖片時,將附檔名轉換成 XXX.webp
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
const path = require("path")

module.exports = function(source, map) {
    let result = source

    if (this.resourceQuery && this.resourceQuery.includes("type=webp") && !this.resource.includes("data:image")) {
        let extname = path.extname(this.resourcePath)
        result = source.replace(extname, ".webp")
    }
    // return result
    this.callback(null, result, map)
}

chainWebpack 設定

  • 套件安裝完成後,vue.config.js 引入套件,並指定webp圖檔存放位置。
  • 引入自訂的 webploader , 並設定 chainWebpackimages 需優先跑 webploader,再跑 url-loaderfile-loader
  • 主要解決 Vue-cli 的 development 下,會因實際不存在 “XXX.webp” 圖檔而導致開發時報錯的問題。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
// ...
const ImageminWebpWebpackPlugin = require("webpack-plugin-image-transform-webp-and-mini")
// ...
chainWebpack: config => {
    // ...
    config.plugins.delete("preload-index")
    config.plugins.delete("prefetch-index")

    // 相關設定建議放於移除 preload-index 與 prefetch-index 之後
    config.plugin("webP").use(ImageminWebpWebpackPlugin, [
        {
            name: "static/img/[name].[hash:8].[ext]",
            logger: false,
            paths: {
                dir: path.resolve(__dirname, "./src/assets"),
                exclude: []
        },
        miniOptions: false
        }
    ])

    let rule = config.module.rule("images")
    rule.uses.clear()
    rule
        .use("./webploader.js")
        .loader("./webploader.js")
        .end()
        .use("url-loader")
        .loader("url-loader")
        .options({
            limit: 4096,
            fallback: {
                loader: "file-loader",
                options: {
                    name: "static/img/[name].[hash:8].[ext]"
                }
            }
        })
    // ...
}

補上是否支援 Webp 的判斷,若支援則於 <html> 補上 class name,供CSS抓取 class name 後改讀 webp 圖片

main.js

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
async function isSupportWebp() {
  return new Promise(resolve => {
    let result = false
    const elem = document.createElement("canvas")
    if (elem.getContext && elem.getContext("2d")) {
      result = elem.toDataURL("image/webp").indexOf("data:image/webp") === 0
    }
    resolve(result)
  })
}

// 使用自定义过滤器
filter(Vue)
;(async () => {
  Vue.prototype.$supportWebp = await isSupportWebp()

  if (Vue.prototype.$supportWebp) {
    document.documentElement.classList.add("webp")
  }
})()

若有使用 vue-lazyload,則補上 vue-lazyload 提供的 webp 相關設定

<img> 使用 v-lazy 時,src的圖片附檔名皆會轉換成 XXX.webp

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
vue.use(VueLazyload, {
    filter: {
        webp(listener) {
            if (vue.prototype.$supportWebp && !~listener.src.indexOf(".webp")) {
                listener.src = listener.src.replace(/\.(png|jpe?g)(\?.*)?$/, ".webp")
                listener.el.setAttribute("data-src", listener.src.replace(/\.(png|jpe?g)(\?.*)?$/, ".webp"))
            }
        }
    }
})

於各個 CSS 中,若 background 使用到需轉 .webp 格式的 img ,補上自行設定的 query ("?type=webp"),以便 webploader 抓該 query 進行轉換圖檔格式

例如:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
.page-wrap-main {
    background: url("~assets/images/main/bg.png");
    background-size: cover;
    width: 100%;
    position: relative;

    .webp & {
        background-image: url("~assets/images/main/bg.png?type=webp");
    }
}

comments powered by Disqus