Our angular 4.3.6 application has lazy-loaded modules, such as Fleet, Maintenance, Car, etc.
My top-level app router looks like this:
const routes: Routes = [
{ path: '', redirectTo: 'fleet', pathMatch: 'full' },
{
path: '',
component: AppComponent,
canActivate: [AuthenticationGuard],
children: [
{
path: 'fleet',
loadChildren: "./modules/fleet.module",
canActivate: [AuthenticationGuard]
},
{
path: 'car/:id',
loadChildren: "./modules/car.module",
canActivate: [AuthenticationGuard]
},
{
path: 'maintenanceProgram',
loadChildren: "./modules/maintenanceProgram.module",
canActivate: [AuthenticationGuard]
}
}
We do have a shared module with generic components (we have lots of them) used throughout the application. There are, however, some components like modals which are shared only by the MaintenanceProgram
and the Car
modules, but not used anywhere else.
In an effort to keep the shared module reasonable, I include these once-reused components only in the MaintenanceProgram
module, export them, and import the MaintenanceProgram
module into the Car
module to have access to the exported components.
Both the Car
and MaintenanceProgram
modules have the following embedded child routes invoked in their respective @NgModule.imports[]
:
Car:
const CarModuleRoutes = RouterModule.forChild([
{
path: '',
component: CarComponent,
canActivate: [AuthenticationGuard]
},
{
path: ':id',
component: CarComponent,
canActivate: [AuthenticationGuard]
}
]);
and Maintenance Program:
const MaintenanceProgramModuleRoutes = RouterModule.forChild([
{
path: '',
component: MaintenanceProgramComponent,
canActivate: [AuthenticationGuard]
}
]);
This is obviously not the correct approach either
- to child routing, or
- to module transclusion
because when I load the Car route, I get the Maintenance Program's default route.
I have tried:
- Changing the order of the import of
MaintenanceProgramModuleRoutes
andCarModuleRoutes
inCarModule
's@NgModule.imports[]
, - Removing the empty path from CarModule.
*Is the only solution to create another shared module that contains the shared components without a router, or is there another, more elegant way to do this?*
This is a reduced example, we actually have many routes and hundreds of components which are reused only twice or three times. This problem will surely persist into the future as the application grows, so I need a scalable and maintainable solution. Creating tens or more extra shared modules is just infeasible.
Actually, when creating a shared module, there is no need to care about modules which uses a little part of the shared module, because Angular's tree-shaking will keep only the used code from an imported module, and remove the rest.
I've prepared a minimal project to demonstrate it: https://github.com/youkouleley/Angular-treeshaking-demo
This project has two lazy modules:
AModule
andBModule
. Both of these modules importSharedModule
.SharedModule
exports three components:AComponent
which is used inAModule
onlyBComponent
which is used inBModule
onlyBothComponent
which is used inAModule
andBModule
Here is what you'll get when
ng build
ing this project in production mode and opening the4-es2015.<hash>.js
file:Note that the
BComponent
from theSharedModule
is missing from theAModule
chunk. Sames goes forBModule
chunk that excludesAComponent
.Also, note that this behavior is obtained when setting
commonChunk
tofalse
in the build options. This option allows you to choose between:false
: Bundle the needed parts of theSharedModule
directly into the lazy modules that imported it. Pro: Even loading time between lazy modules. Con: Some code fromSharedModule
is duplicated between lazy modules chunks, higher app size overalltrue
(default): Have a common chunk that contains the parts of theSharedModule
which are used at least by two lazy modules (the rest is bundled into the lazy modules themselves). Pro: No duplicated code, lower app size overall. Con: The first lazy module is slower to load (it loads the common chunk even if the current route doesn't need it)As a conclusion, Angular build provides optimizations for the
SharedModule
withcommonChunk
set either totrue
orfalse
(depending on your context) you don't really have to worry about yourSharedModule
size. Thus, you don't have to try strange patterns like you did, with hybrid modules fulfilling the feature module role, and the shared module role at the same time.