Angular 8.0 deprecates the string syntax for configuring lazy loaded routes in favor of the EcmaScript dynamic import. The import(specifier)
syntactic form returns a Promise
for the module namespace object. After the Promise
is resolved, we fetch the exported LazyModule
.
Before
const routes: Routes = [{
path: 'lazy',
// The following string syntax for loadChildren is deprecated
loadChildren: './lazy/lazy.module#LazyModule'
}];
After
const routes: Routes = [{
path: 'lazy',
// The new import() syntax
loadChildren: () => import('./lazy/lazy.module').then(m => m.LazyModule)
}];
You can use the new lazy loading syntax in NativeScript Angular as well!
The dynamic import proposal is scheduled for ES2020. It has also been implemented by all major browsers. Since it's quite new, you need to tell TypeScript to use the latest EcmaScript module spec when compiling your code. Open your tsconfig.tns.json file and set the module
property to esNext
:
tsconfig.tns.json
{
"extends": "./tsconfig",
"compilerOptions": {
"module": "esNext",
"moduleResolution": "node"
}
}
Important! The NativeScript Playground doesn't support the dynamic imports yet. For now, stick to the old lazy loading syntax when writing your NativeScript Angular applications in the Playground.
Migrating all your routes to the new syntax can be quite tedious. But you can tell TSLint to do it for you!
First, make sure you have TSLint in your project:
npm install tslint --save-dev
After that, install the angular-lazy-routes-fix
TSLint rule, created by Craig Spence:
npm install @phenomnomnominal/angular-lazy-routes-fix --save-dev
Then, add the rule to your TSLint configuration. If you don't have one, just create a new tslint.json
file:
tslint.json
{
"extends": [
"@phenomnomnominal/angular-lazy-routes-fix"
],
"rules": {
"no-lazy-module-paths": [true]
}
}
Finally, run TSLint with the --fix
flag:
npx tslint -p tsconfig.json --fix
The linter will find all usages of the old lazy loading syntax and replace them with dynamic imports.
The NativeScript runtimes don't support the import()
syntax natively. However, with some help from webpack and Angular, you can use it in your routing configurations. When you build the project with webpack, the following code:
const routes: Routes = [{
path: 'lazy',
loadChildren: () => import('./lazy/lazy.module').then(m => m.LazyModule)
}];
is roughly converted to:
const routes: Routes = [{
path: 'lazy',
loadChildren: requireEnsure('./1.js').LazyModule
}];
The file 1.js is the lazily-loaded bundle for the /lazy route. It contains the NgModule
, called LazyModule
together with all Components
, Directives
and other NgModules
that it depends on.
Sidenote: 1.js don't contain any third-party packages. Instead, all packages from node_modules required somewhere in your application end up in a bundle, called vendor.js.
The lazily-loaded bundle is loaded from the file system by the requireEnsure
function. This function is inserted by webpack. The requireEnsure
function has a different definition if you are building for the browser, Node, or NativeScript.
Sidenote: In NativeScript, the definition of the function is generated from a template that lives in the nativescript-target for webpack.
For NativeScript, the implementation of the requireEnsure
functions simply loads the file with a require()
call, and then - caches it. So, in the end, you never actually ship a dynamic import()
in your application - it's all converted to a good old CommonJS require()
. The NativeScript runtimes implement the require()
function by loading the file from the device file system.
The key takeaways from the whole module loading story are:
import()
.import()
is not supported natively but webpack replaces it with require()
during build.