I have an Angular (version 15.2) workspace, that has an host
app, three modules
and two libraries
.
This structure shows what is being used by what:
host
moduleA
library1
moduleB
library1
library2
moduleC
library1
library2
The host
lazy-loads the three modules in this way:
import {NgModule} from '@angular/core';
import {RouterModule, Routes} from '@angular/router';
const routes: Routes = [
{
path: "a",
loadChildren: () => import("a").then(m => m.AModule)
},
{
path: "b",
loadChildren: () => import("b").then(m => m.BModule)
},
{
path: "c",
loadChildren: () => import("c").then(m => m.CModule)
}
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule {
}
Each of the three modules use library1
and/or library2
, as shown in the structure at the beginning.
The lazy loading works as expected, meaning that when I navigate to "localhost:4200/a" I can see from the network that I only get moduleA.js
(of course with the name generated by Angular).
Naturally I also see the file common.js
that, as I was expecting, contains all the code from library1
used in moduleA
, moduleB
, and moduleC
.
The problem is that this common.js
file also contains the code from library2
, that is only shared by moduleB
and moduleC
. This causes the browser to load code that is useless in that moment and invalidates (although not entirely) the purpose of the lazy-loading system.
Therefore I was wondering if among the angular configurations there is something to add or change in order to obtain a different bundle for each library.
Desired result
The desired result is that when navigating to "localhost:4200/a" the browser would download moduleA.js
and an library1.js
. Then when navigating to "localhost:4200/b", the browser would download moduleB.js
, library2.js
, but not the library1
that has already been downloaded.
What I’ve already tried
I have already explored solutions like the angular options vendorChunk: false
, commonChunk: false
and then tried to use webpack’s SplitChunkPlugin
via the angular json.
With the SplitChunkPlugin
method I was able to obtain a common.js
file that contained only the code of the library1
, that is used by all of the lazy imported modules, but the code of library2
was repeated in moduleB
and moduleC
.
This is what my custom webpack config looked like:
module.exports = {
optimization: {
splitChunks: {
cacheGroups: {
library1: {
test: /[\/]projects[\/]library1[\/]/,
name: 'library1',
chunks: 'all'
},
library2: {
test: /[\/]projects[\/]library2[\/]/,
name: 'library2',
chunks: 'all'
}
}
}
}
};
2
Answers
It turned out that the
SplitChunksPlugin
was indeed what I needed. The plugin wasn't creating two separate bundles because the size of each library wasn't big enough to create a separate bundle, since Webpack has some default min and max sizes for the output bundles.So one way to force Webpack to create separate bundles regardless of their size, is adding the key "enforce: true" to the
SplitChunksPlugin
options:The enforce key
(source).
Might be that one of your dependencies in B and C, is using a component or file that is present in Library2. As far as I know it not customizable, but I just want to post that we can preload the modules in the background, so that it doesn’t matter when which gets loaded.
Angular Docs – Pre loading