Photo by Sangga Rima Roman Selia on Unsplash
Nx Devtools recently released version 13.1, which came with a very handy generator for Angular libraries, which creates secondary entrypoints for your Angular Libraries with ease. In this blog post we will cover why and how we should make use of this new function.
Why secondary entrypoints make sense?
By adding secondary entrypoints, we basically split our Angular libraries into multiple chunks, just like Angular Material
does.
A Example:
// One entrypoint:
import { MatButtonModule, MatCardModule } from '@angular/material';
// With secondary entrypoints:
import { MatButtonModule } from '@angular/material/button';
import { MatCardModule } from '@angular/material/card';
The idea behind it is not new at all, this option already exists since the beginning phase of ng-packagr
. By leveraging secondary entrypoints, we aim better architecture, support of treeshaking, smaller bundle sizes and therefore faster applications.
Demo 👀
To demonstrate the impact, I prepared a little example:
Therefore I created a small greeter
library and an a simple test-app, which just consumes one component out of this greeter library.
Here is the greeter library, which contains a simple greeter
and a fancy greeter
:
// simple-greeter.component.ts
import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
@Component({
selector: 'kauppfbi-blogs-simple-greeter',
template: ` <p>Nice to meet you, {{ name }}!</p> `,
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class SimpleGreeterComponent {
@Input() name!: string;
}
// fancy-greeter.component.ts
...
import moment from 'moment';
@Component({
selector: 'kauppfbi-blogs-fancy-greeter',
template: `
<p>Nice to meet you, {{ name }}!</p>
<p>Today is the {{ time }}</p>
`,
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class FancyGreeterComponent implements OnInit {
time = '';
@Input() name!: string;
ngOnInit(): void {
this.time = moment().format('DD.MM.YYYY');
}
}
Both are exported in the index.ts
file (public api) of the library:
export * from './lib/simple-greeter/simple-greeter.module';
export * from './lib/simple-greeter/simple-greeter.component';
export * from './lib/fancy-greeter/fancy-greeter.module';
export * from './lib/fancy-greeter/fancy-greeter.component';
You probably noticed, that the fancy greeter
imports moment
... you already know, where this leads to.
Our test-app
is also kept simple:
// app.module.ts
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { SimpleGreeterModule } from '@kauppfbi-blogs/greeter';
import { AppComponent } from './app.component';
@NgModule({
declarations: [AppComponent],
imports: [BrowserModule, SimpleGreeterModule],
bootstrap: [AppComponent],
})
export class AppModule {}
// app.component.ts
@Component({
selector: 'kauppfbi-blogs-root',
template: `<kauppfbi-blogs-simple-greeter [name]="name"></kauppfbi-blogs-simple-greeter>`,
})
export class AppComponent {
name = 'Duke';
}
To complete the demonstration, we will use the webpack-bundle-analyzer
to inspect the production bundle of the app:
# build app with production configuration and generate bundle statistics
npx nx test-app:build:production --stats-json
# inspect the bundle
npx webpack-bundle-analyzer dist/apps/test-app/stats.json
This will open up a new webpage containing the chunks of our app:
As you can see, our bundle consist largely of moment
even though we did not consume the fancy greeter
, but the simple greeter. 🐵
How to use the new generator?
Now as we know, why it makes sense to use secondary entrypoints in Angular Libraries, we will look into how to use them.
As it is just one command, it is pretty simple:
npx nx generate @nrwl/angular:library-secondary-entry-point \
--name=my-secondary-entrypoint \
--library=my-library
The command will scaffold a new folder with the name of your entrypint containing a src
folder with a Readme
, a package.json
and a module
exported by a new index.ts
file. In addition, it will adjust the lintFilePattern
of the entire library project and it will create the correct pathMapping
in the tsconfig.base.json
.
Now you can either move existing modules there or create new ones.
In our use case, it was a matter of two minutes to shift the code.
Let's analyze the result:
First of all, we can see that the bundle size was decreased significantly. Okay, this was just a demo and I wouldn't recommend to use moment
nowadays at all. But, more importantly we verified that we only load necessary code and we are able to support proper treeshaking with our solution!
That's it already, happy coding! 🚀
GitHub Repository
All the code is available in a public repository on Github. You can find the greeter library
here and the test-app
here.
Further Links 🔗
Checkout the following blog posts to learn more about seondary entrypoints
:
- Improve SPA performance by splitting your Angular libraries in multiple chunks by Kevin Kreuzer
- Creating Secondary Entry Points for your Angular Library by Christian Ing Sunardi
Top comments (0)