Vue cli 3 with imagemin webpack plugin - vue.js

Using vue cli 3. How to correctly optimize all images png/jpg/svg from src/assets/images using https://www.npmjs.com/package/imagemin-webpack-plugin in vue.config.js:
const ImageminPlugin = require('imagemin-webpack-plugin').default
module.exports = {
configureWebpack: {
devtool: 'source-map',
plugins: [
new ImageminPlugin({
pngquant: {
quality: '90-95'
}
})
]
}
}
But it seems like it's not processing my images, what config setting do i miss?

The imagemin-webpack-plugin by default should optimize PNGs, GIFs, JPEGs, and SVGs pretty well. So even if you use no options (EX: new ImageminPlugin()) you will get all of those. If you want to customize how much it's compressing things, you can always take a look at the docs to customize it.
If your images aren't being optimized, it may be because the plugin has a "fallback" where if the optimized image is LARGER than the original image, it will just use the original one. Sometimes source images just won't compress any better, and falling back to the original seemed like a better default.

By default optimization JPEGs did not occur in my case, everything worked after install imagemin-mozjpeg package
var ImageminPlugin = require('imagemin-webpack-plugin').default
var imageminMozjpeg = require('imagemin-mozjpeg')
...
configureWebpack: {
plugins: [
new ImageminPlugin({
...
plugins: [
imageminMozjpeg({
quality: 85
})
]
})
]
}

Related

How to disable prefetch in VueJS PWA?

When you generate a PWA app using vue ui you can expect the following behavior.
All files you have in your dist folder are precached by browsers.
In other words, when you navigate to your app a browser will quietly download all the files and cache it for further use.
The problem here is that browsers will also download async chunks which could never be used by a user. For example, I have an admin-settings route and a regular user does not need to download it at all.
Now I'm trying to disable this behavior, here's modified vue.config.js file:
module.exports = {
chainWebpack: config => {
config.plugins.delete('prefetch-index')
config.plugins.delete('preload-index')
},
pwa: {
workboxOptions: {
exclude: [/.*/],
runtimeCaching: [{
urlPattern: new RegExp('^https://example.com.*'),
handler: 'CacheFirst',
options: {
cacheableResponse: {
statuses: [200]
}
}
}]
}
},
...
}
Right now everything works as expected and browsers do not prefetch resourses. The problem, however, is that when I upload a new version of a file the app is not updated. Browsers still use the previos version of the file.
I'm stuck, any advice, good sirs?
P.S. Figured it out.
This line caused index.html to be also cached.
urlPattern: new RegExp('^https://example.com.*')
After changing it everything works as expected.
urlPattern: new RegExp('^https://example.com/.+')
Now the problem is that the old version of a cached file is not deleted from cache. The size of cache would grow a lot with each new deployment.
Any advice?

Laravel mix image handling

I have recently switched to laravel-mix from Vue-CLI.
All my images in html stopped working. They were used like this:
<img src="#assets/images/logo.png" alt="Logo">
(#assets is a alias for the resources/assets folder)
With this method, when I look at the html in the browser, I see that it got compiled to [object Module]. So I was able to fix it this way:
<img :src="require('#assets/img/sidebar/logo.png').default" alt="Logo">
But I can't imagine this being the best solution, it seems very hacky and I dont want to add this require().default thing everytime I use an image.
I've tried adding this to the mix.webpackConfig:
module: {
rules: [
{
test: /\.(png|jpe?g|gif)$/i,
use: [
{
loader: 'file-loader',
options: {
esModule: false,
},
},
],
},
]
}
But that doens't do anything, but I don't even know if it works at all to be honest.
Is there a better way to use images with laravel-mix or is require().default really the way to go?
Laravel-mix offers the ability to configure aliases
const path = require('path');
mix.alias({
'#assets': path.join(__dirname, 'resources/assets')
});

How do i exclude a directory with mocking files from webpack build with vue.config.js?

I have a directory called mock at root which contains mocking data that I use to run the app in development mode. I would like to exclude them when i build for production. I notice that it is being added into bundle whenever i run vue-cli-service build and it is bloating my app bundle size.
I am using vue-cli and so I have to work with vue.config.js.
It is not clear from the docs or any answers on the wider web how I can specify which folders/files to exclude from the build.
Here is a snippet of my vue.config.js.
module.exports = {
chainWebpack: (config) => {
config.resolve.symlinks(false)
},
configureWebpack: {
plugins: [
new CompressionPlugin()
]
},
css: {
loaderOptions: {
scss: {
prependData: `#import "#/styles/main.scss";`
}
}
}
}
This is not the perfect solution, but...
If you want to exclude that directory at build time, you can try to use require instead of import. Something like this (source):
if (process.env.VUE_APP_MY_CONDITION) {
require('./conditional-file.js');
}
But be aware of this!

Can use both Tailwind css and Bootstrap 4 at the same time?

My project is currently Vuejs which use BootstrapVue components (seems to use bootstrap 4 css).
I am trying to use Tailwind css for new custom components.
Is it possible to use both of them at same time?
Thank you.
You can solve classes conflict using a prefix
// tailwind.config.js
module.exports = {
prefix: 'tw-',
}
BUT, most likely you will have a problem with normalize.css, which used in #tailwind base
Possible, Yes. Is it recommended, NO.
There are many classes that are gonna contradict with
each other e.g.
.container (Bootstrap)
.container (Tailwind)
.clearfix (B)
.clearfix (T)
And the list goes on...
My advice would be to either stick with BootstrapVue or Tailwind. My personal preference, Tailwind.
Option 1: Adopt or recreate classes
If you only need one or two classes, for example from the color system of Tailwind, you could also copy them.
Some characters would have to be masked, e.g:
// style.css
.hover\:text-blue-900:hover,
.text-blue-900 {
color: #183f6d;
}
That's what I did at the beginning of a project, where bootstrap is the main framework.
If it should be several colors and functions, you can also build this with SCSS quickly. In the long run, however, in my opinion, not the best and cleanest solution.
Example for this:
// style.scss
(...)
#each $name, $hexcode in $tailwind-colors {
.hover\:text-#{$name}:hover,
.text-#{$name} {
color: $hexcode
}
}
}
Full code (Github Gist)
Option 2: Integrate Tailwind
But as soon as more functionalities should be added, or you want to build it cleaner, you can do here with the prefix mentioned by the documentation as Ostap Brehin says.
// tailwind.config.js
module.exports = {
prefix: 'tw-',
}
The normalized definitions can be removed by disabling preflight:
// tailwind.config.js
module.exports = {
corePlugins: {
preflight: false,
}
}
Better check the generated CSS file.
Here is my full tailwind.config.js file:
// tailwind.config.js
module.exports = {
content: [
'./**/*.php',
'../Resources/**/*.{html,js}',
],
safelist: [
'tw-bg-blue-800/75',
{
pattern: /(bg|text)-(blue)-(800)/,
variants: ['hover'],
},
],
prefix: 'tw-',
theme: {
extend: {},
},
corePlugins: {
preflight: false,
},
plugins: [],
}
Yes, you can use Tailwind and Bootstrap together.
However, you need to do some configuration to your tailwind. The first thing is to add a prefix and then turn off the preflight. And if you are not using Tailwind JIT then also make the important as true. If you are using Tailwind JIT then you can use "!" before the class to make it important. For example "!tw-block".
Here is a suitable configuration for Tailwind JIT/CDN:
<script>
tailwind.config = {
prefix: "tw-",
corePlugins: {
preflight: false,
}
}
</script>
If you are not using CDN, use this configuration:
module.exports = {
content: ["./**/*.html"],
prefix: "tw-",
important: true,
corePlugins: {
preflight: false,
}
}
I have written a whole blog on it: https://developerwings.com/tailwind-and-bootstrap-together/
As long as there's no name collision in 2 libs (which I don't think they are), they will work just fine.
But I don't think you should. Because it will break the unification of the project and will make it harder to maintain.

VueJS build started throwing Error: Conflict: Multiple assets emit to the same filename

My app used to work fine until I updated VueJS this morning. Now when I build, it shows the following error:
Error: Conflict: Multiple assets emit to the same filename img/default-contractor-logo.0346290f.svg
There's only one file like this in the repo.
Here's my vue.config.js:
module.exports = {
baseUrl: '/my/',
outputDir: 'dist/my',
css: {
loaderOptions: {
sass: {
data: `
#import "#/scss/_variables.scss";
#import "#/scss/_mixins.scss";
#import "#/scss/_fonts.scss";
`
}
}
},
devServer: {
disableHostCheck: true
}
};
I tried webpack fixes recommended in similar cases, but non helped.
I had the same error when importing SVG files using dynamically generated path in the require statement:
const url = require("../assets/svg/#{value}");
<img src={{url}} />
In this case file-loader processes all SVG files and saves them to the output path. My file-loader options were:
{
loader: "file-loader",
options: { name: "[name].[ext]" }
}
The folders structure has duplicate file names, something like this:
assets
|__ one
|____ file.svg
|__ two
|____ file.svg
In this case file-loader saves both file.svg files to the same output file: build/assets/file.svg - hence the warning.
I managed to fix it by adding [path] to the name option:
{
loader: "file-loader",
options: { name: "[path][name].[ext]" }
}
The answer by #ischenkodv is definitely correct, but because of my inexperience with webpack, I needed a little more context to use the information to fix the problem.
For the benefit of anyone else in the same situation, I'm adding the following details which I hope will be useful.
This section of the Vue.js documentation was particularly helpul:
VueJS - Modifying Options of a Loader
For the TL;DR fix, here is the relevant chunk of my vue.config.js:
// vue.config.js
module.exports = {
// ---snip---
chainWebpack: config =>
{
config.module
.rule('svg')
.test(/\.svg$/)
.use('file-loader')
.tap(options =>
{
return { name: "[path][name].[ext]" };
});
}
// ---snip---
};
In my project it was the flag-icon-css NPM package that was causing the Multiple assets emit to the same filename conflict errors. The above update to the vue.config.js file resolved the problem for me.
I suspect that the regular expression in the test could be tightened up to target just the items in the flag-icon-css package rather than matching all SVG files, but I haven't bothered since it's not causing any adverse effects so far.