How to prevent downloading unused node modules chunks with webpack? - vue.js

I am working on a big app and decided to do some optimizations with webpack.
I used this article by Hackernoon and I can recommend it to anyone: https://medium.com/hackernoon/the-100-correct-way-to-split-your-chunks-with-webpack-f8a9df5b7758
I followed the instructions of the author to separate node modules in multiple webpack chunks:
splitChunks: {
chunks: "all",
maxInitialRequests: Infinity,
minSize: 0,
cacheGroups: {
vendor: {
chunks: "all",
test: /[\\/]node_modules[\\/]/,
name(module) {
// get the name. E.g. node_modules/packageName/not/this/part.js
// or node_modules/packageName
const packageName = module.context.match(/[\\/]node_modules[\\/](.*?)([\\/]|$)/)[1];
// npm package names are URL-safe, but some servers don't like # symbols
return `_npm.${packageName.replace("#", "")}`;
},
enforce: true
},
},
},
However, now all the vendor chunks get downloaded on the first app load, and not just the ones that the current page needs (and is dependent on).
And I have some big libraries like lodash and moment, that I just don't need on the homepage and I don't want them to be downloaded. I want them to be downloaded right after the user has visited a page that is dependent on them.
How can I achieve this?

Found the issue.
Some not-needed shared components, dependent on the external library, are bundled with a needed shared component in the same chunk.
So downloading the needed shared component downloads also the not-needed ones and they trigger the download of the external libraries.

Related

How to extract all the CSS to a single file in Nuxt?

I'm currently building a UI Kit for a client who is using ASP.NET for the main application/backend for their project. The UI Kit that I'm building is created using NuxtJS and I want to compile all of the SCSS files along with Bootstrap into a single compiled CSS file, from what I've gathered on the Nuxt Docs they recommend compiling the SCSS files into single CSS files for each component.
Before I start making a mess of the config file, is there a way to compile them into a single file so the client can just enqueue it on their end? It doesn't need to be the most performative which is why we're going to push it into a singular file.
Here is the part of the doc for Nuxt2, I quote
You may want to extract all your CSS to a single file. There is a workaround for this:
nuxt.config.js
export default {
build: {
extractCSS: true,
optimization: {
splitChunks: {
cacheGroups: {
styles: {
name: 'styles',
test: /\.(css|vue)$/,
chunks: 'all',
enforce: true
}
}
}
}
}
}
This part is not directly written but still available for Nuxt3, so I guess that it should work in a similar approach.
There is only one discussion on Nuxt3's repo, but you may start trying the snippet above already, to see if it fill some of your needs.

Excluding specific entry points from webpack CommonsChunkPlugin

webpack version 2
Hello. I have multiple entry points and each entry point uses vue. And I want to extract the vendor library for caching, so I will use the CommonsChunkPlugin. By the way, can I not use CommonsChunkPlugin only for specific entry points? For certain entry points, it would be nice to have vue in that file.
A and b use vue and jquery extracted as vendor. But in exclude, the file itself must contain vue, jquery. is this possible?
// example
{
entry: {
vendor: ['jquery', 'vue'],
a: './a.js',
b: './b.js',
exclude: './exclude.js', // It does not extract vendor libraries.
},
plugins: [
new webpack.optimize.CommonsChunkPlugin({
name: "vendor",
chunks: ["vendor"]
}),
],
}

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?

Can I disable style chunking while using dynamic import with webpack4 in Vue project?

I am using dynamic import and webpackChunkName in the router of my Vue project.
The imported modules use some Vuetify components.
Once I build the project I see some chunked CSS files which include duplicated content from the vendor CSS file.
How can I prevent this duplication?
I could have finally found out the solution myself.
I have changed chunks from async to all which enables sharing the chunks over all sync and async chunks.
This way no new chunks are created with the content from vendor CSS files.
Check here for more details.
Here's my WebPack configuration from my vue.config.js.
configureWebpack: {
optimization: {
splitChunks: {
cacheGroups: {
vendors: {
chunks: 'all',
},
}
}
},
}

Webpack - optimizarion node_modules & import

Configuring webpack I was wondering something for the optimization. I have two JS files index.js and helper.js. I import helper.js in index.js like that:
import * as helper from 'helper.js';
In these two JS files, I import some node_modules.
Regarding this, to prevent duplication code and caching you can do that:
const path = require('path');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: {
index: './src/index.js'
},
output: {
filename: '[name].[contenthash].js',
path: path.resolve(__dirname, 'dist')
},
optimization: {
runtimeChunk: 'single',
splitChunks: {
cacheGroups: {
vendor: {
test: /[\\/]node_modules[\\/]/,
name: 'vendors',
chunks: 'all'
}
}
}
}
};
If I understand well for the optimization, it's only created one vendor file from the folder node_modules ? It will import everything from the folder node_modules even if I don't use everything (for example the devDependencies) ?
Does it take in account the import of helper.js done in index.js in the vendor ?
Why do they use runtimeChunk in the link provided ?
Or should I do just something like that:
optimization: {
splitChunks: {
chunks: 'all'
}
}
Hope you could help me
You don't need the test as it defaults to node_modules. It will only compile the ones you use. Remember to include that file first before your app one(s) when including them from your html.
It will split all vendor modules out regardless of what file they are included from.
It's worth noting though that since you're importing helper.js into index.js and creating one bundle, webpack will already not duplicate the node_modules but share them, so long as helper.js is not a third party module compiled as a non-es6 module.
In other words webpack will automatically tree shake stuff in your own source files, and es2016 modules in node_modules (not CJS/UMD modules which is the most common).
You only need to split to a vendor bundle if:
a) Your vendor bundle changes with a lot less frequency than your src code (not that common if you're often running npm update)
b) You're producing multiple output files and you want them to share vendor.js / you don't want to declare them as external and make the consumer install them (e.g. a library of components)
P.S. Not exactly sure what runtimeChunk is for but personally I would not specify it (leave it as default) unless you have a good reason.