Module Loading in Angular 2

Typescript uses the ES6 import and export syntax to load modules. The syntax is quite easy to work with. But what happens at runtime is far from simple. Browsers do not fully support ES6 yet. Which means that these import and export statements need to be somehow transpiled into ES5. This is where things get complicated. In today’s post I will discuss how module loading works using three approaches:

  • Using SystemJS
  • Using Webpack
  • Using Webpack through Angular CLI

Using SystemJS

The Typescript compiler will by default transpile import and export statements to CommonJS. For example import is converted into the require() function. For example let’s say that your Typescript code has this:

import { NgModule } from '@angular/core';

The Typescript compiler will transpile this into:

var core_1 = require('@angular/core');

SystemJS provides an implementation of CommonJS. SystemJS adds a bit of extra smarts to make coding easier. The code for the @angular/core module physically sits in node_modules/@angular/core/bundles/core.umd.js within your project’s root. Unless some extra work is done the require() function will fail to locate the file for the module. SystemJS provides that functionality. In the systemjs.config.js file you can state where the file for the @angular/core module can be found:

  System.config({
    paths: {
      // paths serve as alias
      'npm:': 'node_modules/'
    },
    // map tells the System loader where to look for things
    map: {
      // angular bundles
      '@angular/core': 'npm:@angular/core/bundles/core.umd.js',
      //...
    }
    //...
  });

The require() function downloads the JS file using Ajax (XHR) and injects the code into the DOM. This means, JS files are downloaded on demand as and when needed.

SystemJS also provides a polyfill for the System.import() function which is used to dynamically load a ES6 module from the browser.

In summary anywhere you see an import statement you can mentally map it to the require() function. This is easy to comprehend and work with. But there is a problem. This model does not work very well if you wish to combine multiple JS files into a few. As it is, each JS file is downloaded separately. In Angular 2 even the simplest of applications may download over 30 JS files. Webpack provides a solution for this. We will discuss that next.

Using Webpack

The main motivation for using Webpack is to be able to concatenate your Angular 2 JS files into a small number of JS files. Presumably this can speed up application load.

The order in which files are combined has to take into account the dependency. For example if file A uses code from file B the the concatenated file needs to include B first before A. A very cool feature of Webpack is that it can crawl through your Typescript code and look at all the import statements and automatically deduce the dependency tree. It can then combine the files in the correct order.

Webpack then mainly does two things:

  1. Transpiles your Typescript code into JS and then concatenates all your JS code in the correct order. You can combine all your code into a single JS file. Or you can group them into multiple files.
  2. Transpiles the import and export statements so that code can be loaded at runtime from the concatenated files.

At the time of this writing Typescript has no direct support for Webpack style module loading nor does it seem to have any plans for it. Webpack has to do the work to appropriately convert the import and export statements. For example, the same Typescript code as before:

import { NgModule } from '@angular/core';

Gets transpiled into something like this:

var __WEBPACK_IMPORTED_MODULE_0__angular_core__ = __webpack_require__(1);

A combined JS file is loaded using a single HTTP request. Individual import statements do not require any additional HTTP calls.

Currently, most of Angular 2 documentation and sample code uses SystemJS. However, a very good documentation exists for using Webpack with Angular 2. This documentation shows how to configure Webpack using the webpack.config.js file and how to do a build (concatenate TS files) using packge.json.

Using Webpack through Angular CLI

Angular CLI creates an Angular 2 project configured to use Webpack instead of SystemJS. The CLI adds a few extra smarts that makes it easier to work with Webpack. For example, there is no real need to create your own webpack.config.js file. Nor do you have to specify in package.json how to do a build. The ng build command takes care of that. If you choose to use Webpack then Angular CLI is highly recommended.

Summary

Making a choice between SystemJS and Webpack can be a hard one. If you have decided to using Angular CLI then it makes sense to use Webpack since that’s the direction they have taken.

SystemJS seems to be more in tune with ES6 and Typescript. I find it a bit unnerving that the code generated by Webpack differs from the Typescript compiler. I also like the on demand loading behavior of SystemJS. At the same time I find the syntax of systemjs.config.js hard to work with.

The need to concatenate JS files may not be as important with HTTP 2. This has been discussed extensively elsewhere. So, if you are using HTTP 2 then SystemJS may perform just as good as Webpack.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s