Angular routing is the most powerful of the three AVR frameworks (none of them are), and with its multi-layer nested routing, routing daemons, and lazy loading of routing modules, it is perfectly capable of meeting SPA needs of all complexity. Let’s demystify this powerful feature and start with lazy load routing today.

Configure lazy routing in Angular projects

Here is a typical configuration using lazy loading:

  • The route with loadChildren is imported to the AppModule. LoadChildren is the key to everything, and would not work if it were written as component:

const appRoutes = [
    {
        path: 'button',
        loadChildren: './button/demo.module#ButtonDemoModule'
    }
];

@NgModule({
    declarations: [AppComponent],
    imports: [
        RouterModule.forRoot(appRoutes)
    ],
    bootstrap: [AppComponent]
})
export class AppModule {
}Copy the code
  • Configure routing navigation in ButtonDemoModule. The following configuration is plain and simple, just to give you a context, the key part is loadChildren!

const buttonDemoRoutes = [
    {
        path: 'basic', component: ButtonBasicDemoComponent
    }
];

@NgModule({
    declarations: [ButtonBasicDemoComponent],
    imports: [
        RouterModule.forChild(buttonDemoRoutes)
    ]
})
export class ButtonDemoModule {
}Copy the code

The effect of this configuration is that the /button layer is lazily loaded, while the /button/* layer is non-lazily loaded. Here is an online demo http://rdk.zte.com.cn/component/, a key part of the source code here at http://t.cn/RKV5jph

If you like Jigsaw component library (https://github.com/rdkmaster/jigsaw), please help me point the stars encourage us

Here’s a diagram for lazy people:

Routing lazy load processes in Angular

We went through Angular routing code and found that our configuration in the routing module is actually a Route array. The routing component finds the configuration of the Route based on the browser URL and the array, including the component, routing socket, daemons, data, and so on.

The key step in Route lazy loading is that when loadChildren configuration information is present in the Route array, the routing module calls the NgModuleFactoryLoader service, initiates a request to download the corresponding package file, and then loads the component view of the Route.

Here’s a rough process flow chart:

How is routing lazy loading implemented

At first we took a detour, thinking it was a lazy loading of the routing module implementation, but we couldn’t find a specific implementation in the routing implementation code, which we later found was entirely due to WebPack and Angular-CLI.

It’s important to start with the fact that Angular-CLI is packaged with WebPack, so it’s worth looking at how webpack is packaged and lazily loaded.

How does Webpack pack and lazily load

Take webpack’s lazy loading of a third-party plug-in referenced in JS as an example, using require.ensure in JS code, Webpack can be sliced by require.ensure to distinguish normal require.

require.ensure([], (require) => {

   require("bootstrap/dist/css/bootstrap.min.css");

   require("eonasdan-bootstrap-datetimepicker/build/css/bootstrap-datetimepicker.min.css");

require("eonasdan-bootstrap-datetimepicker"); }, 'datepicker'); // datepicker is the defined slice package nameCopy the code

Configure the slice name in webpack.config.js

Output: {path: helpers.root('dist'), filename: '[name].[hash].js', chunkFilename: '[name].[hash].chunk.Copy the code

Webpack slicing results:

The actual running effect is as follows. You can see that the datepicker.chunk.js file was lazily loaded

Inquisitive fans will ask, how does WebPack load slicing files lazily? We can take a look at the packed papers.

The webPack generated file contains three important global functions

  • webpackJsonp

  • __webpack_require__

  • __webpack_require__.e

The following describes their roles one by one.

webpackJsonp

Webpack uses JSONP technology to define the function webpackJsonp in the browser, and then download js encapsulated with webpackJsonp from the back end, and the browser can immediately execute the JS file. WebpackJsonp source code is as follows:

(function(modules) {

   window["webpackJsonp"] = function webpackJsonpCallback(chunkIds, moreModules, executeModules) {

// add "moreModules" to the modules object, // then flag all "chunkIds" as loaded and fire callback /*...... */ for(moduleId in moreModules) {

if(Object.prototype.hasOwnProperty.call(moreModules, moduleId)) { modules[moduleId] = moreModules[moduleId]; }}

       /*......*/    }; })([])Copy the code

WebpackJsonp takes three arguments

  • ChunkIds is the package file identifier;

  • MoreModules are modules that come with package files, corresponding to moduleId one by one. WebpackJsonp copies modules from package files to global modules.

  • ExecuteModules are modules to execute immediately;

Webpack files are wrapped in webpackJsonp functions, similar to:

webpackJsonp([12, 33],{

   111:    (function(module, exports) {

/ *... * /}),

   112:    (function(module, exports, __webpack_require__) {

/ *... * /}),

   /*......*/

})Copy the code

__webpack_require__

Load modules from global modules based on the moduleId.

__webpack_require__.e

By dynamically inserting script tags, download the corresponding chunk package file, you can see the source code

__webpack_require__.e = function requireEnsure(chunkId) {

/ *... */ var head = document.getElementsByTagName('head')[0];

   var script = document.createElement('script');    script.type = 'text/javascript';    script.charset = 'utf-8';

/ *... */ script.src = __webpack_require__.p + "" + chunkId + ".chunk.js";

/ *... */ head.appendChild(script);

   return promise; }Copy the code

How is Angular-CLI packaged

Angular-cli implements custom configuration for WebPack. Instead of using require.ensure for slice packaging, Angular-CLI makes WebPack recognize the loadChildren keyword in the router for packaging. Let’s take a look at the packed file.

main.bundle.js

var map = {

   "./button/demo.module": [

919, 11],

   "app/demo/demo-list": [

923, 23};

function webpackAsyncContext(req) {

   var ids = map[req];

if(! ids) {

       return Promise.reject(new Error("Cannot find module '" + req + "'."));    }

Return __webpack_require__. E (ids[1]).then(function() {// __webpack_require__.

       return __webpack_require__(ids[0]);    }); };Copy the code

The url is the loadChildren parameter configured by the user in the route. Ids [1] represents chunkId, which is the corresponding package file. Ids [0] represents moduleId, which is the module to be loaded. In the webpackAsyncContext function, __webpack_require__.e is downloaded from the back end to the chunk package file via chunkId, and __webpack_require__ is loaded into the corresponding module via moduleId.

Slice package 11.chunk.js corresponding to button-Demo-Module

Aiaa webpackJsonp ([11] and {

   919: (function(module, __webpack_exports__, __webpack_require__) {

       /* harmony export (binding) */        __webpack_require__.d(__webpack_exports__, "ButtonDemoModule", function() {

           return ButtonDemoModule;

       });

       var ButtonDemoModule = (function () {

           function ButtonDemoModule() {            }

           return ButtonDemoModule;        }());    }) })Copy the code

The final conclusion

The user enters the following URL in the browser

http://localhost:4200/button/basicCopy the code

The RouterMoudule calls the Angular-CLI webpackAsyncContext function through the injected NgModuleFactoryLoader. WebpackAsyncContext gets the corresponding chunkId through the map, calls __webpack_require__. E to dynamically download the corresponding chunk package file. After downloading, just like using ordinary modules, Call __webpack_require__ to execute the corresponding module, which is then available globally.

This is how Angilar – CLI loads the loadChildren of the route.

After reading this article, we see that Angular code is so tightly tied to angular-CLI, so please give me one good reason not to use angular-CLI to initialize your projects!

digression

These articles are our in developing the technology in the process of the Jigsaw puzzle, if you enjoyed this post, please help to the Jigsaw puzzle (https://github.com/rdkmaster/jigsaw), the engineering point of the stars encourage us, This gives us more incentive to write similarly high-quality articles. Jigsaw is in its infancy and needs your attention.