Search code examples
vue.jswebpackvue-cliwebpvue-cli-3

Create webp images (as alternative version) from original images in Vue CLI 3


I have simple Vue project on newest Vue CLI 3 (I'm newbie on both). In src I have jpg / png images in 100% quality. By default cli while build will make dist directory with my images adding hash (image.jpg to image.7a97ca45.jpg) but without any compression.

So I have added imagemin-webpack-plugin and imagemin-mozjpeg and imagemin-pngquant to get optimization. And it works. In dist now I have much smaller jgp's and png's.

const ImageminPlugin = require('imagemin-webpack-plugin').default
const ImageminMozjpeg = require('imagemin-mozjpeg')
const ImageminPngquant = require('imagemin-pngquant')

module.exports = {
  configureWebpack: {
    plugins: [
      new ImageminPlugin({
        plugins: [
          ImageminMozjpeg({
            quality: 70
          }),
          ImageminPngquant({
            quality: '80-90'
          })
        ]
      })
    ]
  }
}

But now I want to have also .webp images created from original jpg's. So i should get for example both image.7a97ca45.jpg and image.7a97ca45.jpg.webp (or image.7a97ca45.webp) so then I can detect browser and serve correct format.

But I do not understand how I can force webpack in vue.config.js to create these files. I tried for example imagemin-webp-webpack-plugin but this does nothing ("imagemin-webp-webpack-plugin: 0 MB saved - in console"). I also tried to add imagemin-webp to imageminplugin config but this also don't work because I do not have original webp in my source, just want to convert new one.

All topics I could find about webp was for normal webpack or older vue-cli, and best I achieved was copying whole images directory with optimization or created webp images from already compressed jpg's (not originals). But i want to keep normal loader with all hashing etc. What's best approach to this?


Solution

  • Answering my own question from 2 years because it has some views.

    Short answer if You are in hurry:

    If You are using Nuxt with Vue then use nuxt-optimized-images and for images path use query ?webp instead .webp extension. Done.

    Long answer:

    I'm using Nuxt and my answer is for this, but It for sure can be done on raw Vue just by using webpack loaders manually.

    So there is module nuxt-optimized-images which simply combines multiple webpack loaders and imagemin with plugins for every format. If You are not using Nuxt, or even Vue, then simply check in docs or source for dependencies and install/configure webp support manually.

    It has webp-loader that we need build-in. The thing is, how to use it correctly with nuxt-optimized-images. We cannot simply type somewhere in code path image.webp or image.jpg.webp because webpack will throw error that file does not exist (this is problem that I also had before). Instead we need to use query ?webp in file path, for example <img src="@/assets/images/photo.jpg?webp" />. Library will convert requested file to webp and everything works. Great thing about this compared to other solutions is that it does NOT convert all png/jpg images in project, but just files that we selected. Thank's to this compiling is faster. (docs)

    Webp support and detection

    If You are reading this, then You wanna support both .webp and .jpg/.png. In 2018 it was normal practice since this format was supported only by Chrome. But today (2020) pretty much all major browsers handles webp, including one from Microsoft (Edge). IE will never support it (but c'mon...) and Safari is still behind but in latest Mac OS it was also introduced. With time coverage can be only greater. So is supporting multiple formats worth the hassle? Caninuse.com.

    It is not best idea for every site, but maybe consider this in Your scenario. Let's say we have .jpg and .webp as site background. Do we really need .jpg fallback for old browsers? Maybe just making solid color in CSS as fallback will be better/easier/faster solution?

    BUT, if we need to support both .webp and .jpg/.webp formats then it can be done in multiple ways in JS (so in Vue) by detecting user agent, headers etc. It will be problematic with Nuxt (SSR - Server Side Rendering) so best solution that will work everywhere (not just in Vue) is HTML5 <picture> tag (docs).

    <picture>
      <source srcset="@/assets/images/screen.png?webp" type="image/webp">
      <img src="@/assets/images/screen.png" alt="Screen">
    </picture>