ng-router-loader
Version:
Webpack loader for `NgModule` lazy loading using the angular router
188 lines (139 loc) • 6.62 kB
Markdown
Webpack loader for `NgModule` lazy loading using the angular router
[](https://travis-ci.org/shlomiassaf/ng-router-loader)
[](https://badge.fury.io/gh/shlomiassaf%2Fng-router-loader)
## Installation
`npm install ng-router-loader --save-dev`
OR
`yarn add ng-router-loader --dev`
# V 2.0.0 BREAKING CHANGES:
Version 2.0.0 introduce support for the [import()](https://github.com/tc39/proposal-dynamic-import) construct.
`import()` is [not yet implemented](https://github.com/Microsoft/TypeScript/issues/12364) in TypeScript.
TypeScript does not ignore it but transpile it to something else which breaks the code.
To use the `import()` construct the loader must run **AFTER** the typescript transpilation process,
this is after the `awesome-typescript-loader` in the example below.
Running after TS also means all code generators now emit ES5 code.
> Webpack 1 users can't use `async-import` as it's not supported in version 1.
Webpack 2 users can use it as long as they are running on webpack > 2.1.0 beta28
# V 2.1.0 BREAKING CHANGES:
`ng-router-loader` now uses AST to parse the module.
Using AST provides a more accurate detection of the `loadChildren` property.
## Webpack integration
Add the `ng-router-loader` to your typescript loaders chain
### Webpack 1
```
loaders: [
{
test: /\.ts$/,
loaders: [
'ng-router-loader',
'awesome-typescript-loader'
]
}
]
```
### Webpack 2
```
module: {
rules: [
{
test: /\.ts$/,
use: [
{
loader: 'ng-router-loader'
options: {
/* ng-router-loader options */
}
} ,
'awesome-typescript-loader'
]
}
]
}
```
## Lazy Loading
Use the `loadChildren` API with **any webpack resolvable** path to reference your lazy loaded angular module.
Use `#` as a delimiter and write the `NgModule` class name.
```ts
import { Routes } from '@angular/router';
export const ROUTES: Routes = [
{ path: 'detail', loadChildren: () => '../my-ng-modules/details#DetailModule' },
];
```
> The delimiter is configurable.
> Query parameters (details#DetailModule?loader=sync) are added after the delimiter.
This behaviour might change, supporting both pre & after.
## Synchronous Loading
For synchronous module loading, add the sync=true as a query string value to your loadChildren string. The module will be included in your bundle and not lazy-loaded.
```ts
import { Routes } from '@angular/router';
export const ROUTES: Routes = [
{ path: 'detail', loadChildren: () => '../my-ng-modules/details#DetailModule?loader=sync' },
];
```
> The Synchronous example uses a resource specific loader option, you can also set a global loader option.
## Configuration
Please read [the documentation](https://shlomiassaf.github.io/ng-router-loader)
# In detph
## @angular/router
The `@angular/router` provides an API for deferred `NgModule` loading, this is a simple API that accepts a function that returns an `NgModule` class.
**Project structure**
```
├── project-root/
│ ├── app
│ │ ├── app.routes.ts
│ ├── my-ng-modules
│ │ ├── details
│ │ │ ├──index.ts
│ │ │ ├──details.module.ts
│ │ │ ├──details.component.ts
```
> DetailModule is defined in `details.module.ts` and exported in `index.ts`
**app.routes.ts**
```ts
import { Routes } from '@angular/router';
import { DetailModule } from '../my-ng-modules/details';
export const ROUTES: Routes = [
{ path: 'detail', loadChildren: () => DetailModule },
];
```
The `@angular/router` will not invoke the function until the path is active, this is the how lazy loading is done.
## The loader
The example above works just fine but it includes a hard reference to the `DetailModule`.
Having a reference results in adding the file containing the module into the bundle.
To achieve lazy loading we need to write the code in a lazy loading code-style that webpack understand.
`ng-router-loader` abstracts the complexity and provides an easy approach using a string reference.
In the background the loader will translate the string to code.
The string reference is the reference you use when you `require` or `import`.
Any string that resolves with `require` or `import` can be used and the same rules apply with 1 addition, the string reference requires must provide the name of the `NgModule` exported.
Using the same example above:
**app.routes.ts**
```ts
import { Routes } from '@angular/router';
export const ROUTES: Routes = [
{ path: 'detail', loadChildren: () => '../my-ng-modules/details#DetailModule' },
];
```
> It's that easy!
## A word about the `angular-router-loader`
The `angular-router-loader` ("ARL" from now) came out with angular final when AOT was still blurry and not enough information was out there.
This made it very limited in it's capabilities, while using it I reached some dead ends that **ARL** did'nt handle.
Another issue I had is that **ARL** forced me to structure my app in a certain way which was not webpack oriented. A loader should be transparent to the developer.
I started fixing things and quickly understood that a rewrite is required.
Here are some of the key points:
- **Module resolution**
**ARL** use the file system to resolve URIs, this makes it impossible to use the goodies webpack `resolve` provides,
such as **barrels**, **aliasing**, **custom module directories** and more, see [webpack resolve](https://webpack.js.org/configuration/resolve/).
`ng-router-loader` uses webpack to resolve modules so everything webpack resolves will work.
- **AOT re-exports**
**ARL** can't handle re-exported `NgModule` symbols in AOT mode.
The example above shows the `index.ts` file exporting the `DetailModule` defined in a different
file, this is a tricky scenario that requires symbol tracking and it will result in an unknown module import created by **ARL**
`ng-router-loader` performs a deep metadata search to extract the right import.
- **Custom code generators**
`ng-router-loader` code generation is plugin based, you can provide a custom code generator that fits your use case.
- **Typescript based**
## TODO
[x] Smart detection, use AST to detect ROUTE API.
## Credits
[angular-router-loader](https://github.com/brandonroberts/angular-router-loader)
Learned a lot reading the code!