By Danyal Zia | 2/1/2017 | General |Intermediate

Optimizing Angular 2 Applications

Optimizing Angular 2 Applications

(Note: This article assumes that you have read all my previous articles on AngularJS 2, as it will use several jargons that won’t be explained here. This is the final article in the series of AngularJS 2).

Highlighting the stuff they don’t want you to know.

If you have been reading my articles on AngularJS 2, then you know that I have pretty much covered everything pertaining to the web development on AngularJS 2 through TypeScript, but you might be wondering what are the correct procedures to bundle the application for production? It doesn’t help to search for guides to bundling online, because most tutorials don’t cover the optimization aspects of the application.

We know that TypeScript transpiles to JavaScript through a compiler, so that when you bundle for the final production app, you also need to take notice of the size of the application, the complexity of the application and ease of use for the users.

Fortunately, AngularJS 2 provides several tools out-of-the-box that allow you to optimize the application to suit your needs. For instance, the cool Feature Modules make your code easy to read and write as well as more modular. Lazy loading allows you to load certain modules a bit later than other modules to achieve a certain purpose. Ahead-of-Time (AOT) compilation allows you to compile the application before running it in the browser to decrease the loading time, and many more!

So, in this article, I am going to show you how you can optimize the applications and finally bundle your awesome work for the production. Let’s get started!

Optimizing an AngularJS 2 Application – Feature Modules, Lazy Loading, Ahead-of-Time Compilation

Before we talk about the features of AngularJS 2 that make your application optimized, let’s first understand what Optimization is really. Optimization is the process of making something fully perfect, functional or as effective as possible. It is applied in many contexts, for example, SEO (Search Engine Optimization) is a technique or a process to increase the amount of visitors to a particular website by obtaining high placement in the search engines.

Such Optimization is applied in web development domain as well. For example, recent HTML 5 versions deprecated the need of Flash for video and audio streaming, so websites still using the Flash for audio/video need to be “optimized” through the replacement of HTML5 over Flash, because HTML 5 is dramatically faster than Flash!

In the same way, AngularJS 2 provides several tools for the optimization which we are going to see.

 

Feature Modules

AngularJS 2 support Feature Modules that allow you to write short code in a more modular manner. So, let’s first understand what Feature modules really are.

When you create new components, you need to import those components in your root component, which is AppComponent in most cases, although Angular CLI (in general) seems to import those components for you automatically. There are times when your component count becomes huge (think of User-interface components!), and it may seem cumbersome to import all those components again and again whenever you need them.

Feature Modules provide you the option to create a module that encapsulates several components together, and so the module can be used whenever those components are required.

Every module has the @NgModule decorator, so Feature Modules are the same as other Modules, but it is called “Feature” because of its purposes. The rest of the modules are called App Modules which get bootstrapped (so there MUST be ONE App Module in your application!), while the Feature Modules simply get imported in App Modules.

Think about BrowserModule, HttpModule, ReactiveFormsModule…. what are these modules? Basically, they contain several components (or modules!), but you don’t need to import all of those components in the “declaration” array, as you can simply import the module in “import” array of your component which fetches all the components belonging to the module.

Doesn’t make sense? Awesome! So, let’s create a new project for the demonstration:

$ng g component gui

By default, component creation doesn’t create the respective module, so let’s create it manually. In “app/gui/” folder, create a new file “gui.module.ts” and copy the following content:

gui.module.ts

import { NgModule } from "@angular/core";
import { FormsModule } from "@angular/forms";
import { GuiComponent } from "./gui.component";
import { GuiSidebarComponent } from "./gui-sidebar.component";
import { guiRouting } from "./gui.routing";

@NgModule({
   declarations: [GuiComponent, GuiSidebarComponent],
   imports: [FormsModule, guiRouting]
})

export class GuiModule {}

Now, create a new file “gui-sidebar.component.ts” along with its HTML component (although that will be empty). Copy the following content:

gui-sidebar.component.ts

 

import { Component } from '@angular/core';
import { GuiService } from "./gui.service";
@Component({
 selector: 'app-gui-sidebar',
 templateUrl: 'gui-sidebar.component.html'
})
export class GuiSidebarComponent {
 constructor(private guiserv: GuiService) {}
}

Now, copy the content to “gui.component.ts”:

gui.component.ts

 

import { Component, OnInit } from '@angular/core';
import { GuiSidebarComponent } from "./gui-sidebar.component";
import { GuiService } from "./gui.service";
@Component({
 selector: 'app-gui',
 templateUrl: 'gui.component.html'
})
export class GuiComponent implements OnInit {
 constructor(private guiserv: GuiService) {}
 ngOnInit() {
 }
}

Finally, create a new file “gui.service.ts” and copy the following content:

gui.service.ts

export class GuiService {
 constructor() {}
}

We are using Routes here as well, but I will talk about it later, so ignore the Routes for now.

Create a new file “index.ts” and copy the following contents:

index.ts

 

export { GuiSidebarComponent } from './gui-sidebar.component';
export { GuiService } from './gui.service';

So, let’s add everything into our root module “app.module.ts”. Copy the following contents:

app.module.ts

 

import { NgModule } from '@angular/core';
import { HttpModule } from '@angular/http';
import { BrowserModule } from '@angular/platform-browser';
import { GuiService } from "./gui/gui.service";
import { routing } from "./app.routing";
import { AppComponent } from "./app.component";
@NgModule({
   declarations: [
       AppComponent
   ],
   imports: [
       BrowserModule,
       HttpModule,
       routing
   ],
   providers: [GuiService],
   bootstrap: [AppComponent]
})
export class AppModule {
}

Please note that I haven’t provided any functioning code to the GUI component(s), because my purpose is to demonstrate the Feature Modules, meaning you can implement any code or logic in the same manner, and it will work!

Many directives such as ngIf and ngFor are provided in BrowserModule, so you need to provide it, but it must only be used once in the AppModule, so you don’t need to import this module again in your other components or modules because AppModule is the root module, and whatever is imported there is available to all the other modules.

But what about the component declared in the routes? Well, you need to import the component for the routes, because feature module don’t work for routes, but there is thing called “Lazy Loading”, through which you can use the feature module for routes (and child routes!).

Lazy Loading

Firstly, understand whatever you import in AppModule doesn’t get distributed to the whole application, so for the routes you need to either inject the service (by creating the separate service for the routes!) or use the lazy loading, which loads the modules only when they are needed. If you remember the earlier code,  I added the “routing” component to the “imports” in AppModule, so the router-outlet will show the errors because it’s not aware of it.

So, for example, copy the following code to “app.component.html”:

app.component.html

 

<h1>
 {{title}}
 <router-outlet></router-outlet>
</h1>

And you will see the errors on the console of your browser!

But, you can make the router-outlet aware of your routes through lazy loading. So, for example, let’s say you have a GuiModule and GuiSidebarModule, and so you will lazy load these modules through the following code:

 

const APP_ROUTES: Routes = [
 {path: 'gui', loadChildren: 'app/gui/gui.module#GuiModule'},
 {path: 'gui-sidebar', loadChildren: 'app/gui/gui.module#GuiModule'},
 {path: '', component: AppComponent}
];

Here, you need to remember this syntax, basically, we are appending the relative path of the module that we are using as routes, and then we are appending ‘#’ with the name of the Module!

We need to export these routes to the rest of the application:

export const routing = RouterModule.forRoot(APP_ROUTES);

Create a new file “app.routing.ts” in the “src/” folder and copy the following contents:

app.routing.ts

 

import { RouterModule, Routes } from '@angular/router';
import { AppComponent } from "./app.component";
const APP_ROUTES: Routes = [
 {path: 'gui', loadChildren: 'app/gui/gui.module#GuiModule'},
 {path: 'gui-sidebar', loadChildren: 'app/gui/gui.module#GuiModule'},
 {path: '', component: AppComponent}
];
export const routing = RouterModule.forRoot(APP_ROUTES);

Now, create another file “gui-routing.ts” in “src/gui/” folder and copy the following contents:

gui-routing.ts

 

import { Routes, RouterModule } from "@angular/router";
import { GuiComponent } from "./gui.component";
const GUI_ROUTES: Routes = [
   { path: '', component: GuiComponent}
];
export const guiRouting = RouterModule.forChild(GUI_ROUTES);

….and that’s all! The application can run now and won’t show errors of the router-outlet! Try using the same HTML code I wrote earlier in “app.component.html” and visit different routes, i.e., “/gui”, “/gui-sidebar”, and it will work just fine!

Shared Modules and Core Modules

You can even use the Shared Modules and also Core Modules. What are these? Well, Shared Modules can be used if you have two Feature Modules that share the same functionality (i.e., directive, component, etc.), so you can import the shared component in a separate module, so whatever file(s) needs to access the shared component, it can access it through the Shared Module!

Imagine your application has two components: GUI and Menu-Bar. And, both of these contain the Feature Modules that use the “CommonModule” from ‘@angular/common’. In this case, you need to import CommonModule in both of these modules, but you can create a separate module (basically a shared module!) that contains this module, and those two components will be using this shared module!

Create a file “shared.module.ts” and copy the following contents:

shared.module.ts

 

import { NgModule } from "@angular/core";
import { CommonModule } from "@angular/common";
@NgModule({
   exports: [CommonModule]
})
export class SharedModule {}

Here, we are “exporting” the CommonModule through the “exports” array. You can now import this module in your “imports” array of the module. You might be asking why we didn’t import this module in our Shared Module? Well, because we are not using this module in this module, as we are only interested in the other modules using this module! Makes sense? Huh!

So, what about Core Modules? Basically, it allows you to “outsource” (or export) those components that are being used by both App Modules and Feature Modules. This can shorten your App Modules (app.module in our case). Let’s say you have a component, “ThemeComponent” and this is being used by your App Module (and also your Feature Modules), then you can “export” it in your Core Module.

Create a new file “core.module.ts” in your “src/” folder and copy the following contents:

core.module.ts

 

import { NgModule } from "@angular/core";
import { ThemeComponent } from "./theme.component";
@NgModule({
   declarations: [ThemeComponent],
   exports: [ThemeComponent]
})
export class CoreModule {}

This of course assumes that you have created “theme.component.ts” somewhere (which you can create just for testing!). And, now, in your “app.module.ts”, you can “import” the Core Module.

Ahead-of-Time Compilation

In AngularJS 2, you can compile the applications in two ways: Just-in-Time (JIT) Compilation and Ahead-of-Time (AOT) Compilation. Till now we have been working in AngularJS 2, but what you may have not realized is that we have used the JIT compilation which compiles the application at run-time, so you can see the changes on the browser the moment you modify something in your code. However, the problem with JIT is that it may take longer to render the views (especially the files that contain large amounts of code!), and so in order to tackle the problem, Ahead-of-Time Compilation (AOT) was introduced.

AOT allows you to run the pre-compiled version of your application, so that the browser loads the executable code faster. Before our application renders on the browser, the compiler needs to transform the TypeScript code to JavaScript which takes longer, but this must be done by the Angular CLI because we are using TypeScript.

With AOT, you can build the application offline (in a kind of step-by-step build), and once the compilation is done, we can upload the compiled version. This may lead to performance improvements (such as faster loading times) and also reduce the size of the program.

Oh, another cool feature of the AOT is Tree Shaking, which does nothing but reads the dependency graph from top to bottom and “shakes” the unused code. It greatly reduces the downloaded size of the application, so for example, if you are not using code from @angular/forms then it will not download Forms-related code. Also, AOT and Tree Shaking are different processes, but AOT helps the code, making it more shakable by converting the HTML code to JavaScript so the Tree shaker can process more of our application.

In order to do the AOT compilation, create a new file “tsconfig.aot.json “ in the “src/” folder and copy the following content to newly created file:

tsconfig-aot.json

 

{
 "compilerOptions": {
   "target": "es5",
   "module": "es2015",
   "moduleResolution": "node",
   "sourceMap": true,
   "emitDecoratorMetadata": true,
   "experimentalDecorators": true,
   "lib": ["es2015", "dom"],
   "noImplicitAny": true,
   "suppressImplicitAnyIndexErrors": true
 },
 "files": [
   "app/app.module.ts",
   "app/main.ts"
 ],
 "angularCompilerOptions": {
  "genDir": "aot",
  "skipMetadataEmit" : true
}
}

The only important thing here that is changed from “tsconfig.json” is the “module” that is set to es2015. Basically, we will use “ngc” compiler instead of the “tsc” compiler that is used for the TypeScript.

Then you need to install the compiler:

npm install @angular/compiler-cli @angular/platform-server --save

 

And now for the AOT compilation, do the following:

node_modules/.bin/ngc -p tsconfig-aot.json

 

It will create NgFactory files in “aot” folder that you specified in “genDir”.

 

Before running the application through AOT, you need to change the Bootsrapping method as well. Following is the new code for boostrapping:

 

 

import { platformBrowser }    from '@angular/platform-browser';
import { AppModuleNgFactory } from '../aot/app/app.module.ngfactory';
platformBrowser().bootstrapModuleFactory(AppModuleNgFactory);

Things that are changed here is that we are using “platformBrowser()” instead of “platformBrowserDynamic()” and that we are using “AppModuleNgFactory” instead of “AppModule”. This code needs to altered in “main.ts” in your “src/” folder.

 

Also, you need to set “window.module” to “aot” in your index.html like the following:

 

<script>window.module = 'aot';</script>

Now, we need to use the Tree Shaking utility “Rollup” which does nothing but statically analyzes the application by following the trail of import and export statements. So basically, you get the final code bundle that is exported, but never imported. For this reason exactly, we used the es2015 in our “tsconfig-aot.json” because it supports import and export statements!

So, let’s install the Rollup dependencies:

 

npm install rollup rollup-plugin-node-resolve rollup-plugin-commonjs rollup-plugin-uglify --save-dev

And, now we will create a configuration file “rollup-config.js” in the root directory. Copy the following contents to the file:

rollup-config.js

 

import rollup      from 'rollup'
import nodeResolve from 'rollup-plugin-node-resolve'
import commonjs    from 'rollup-plugin-commonjs';
import uglify      from 'rollup-plugin-uglify'
export default {
 entry: 'app/main.js',
 dest: 'dist/build.js', // output a single application bundle
 sourceMap: false,
 format: 'iife',
 plugins: [
     nodeResolve({jsnext: true, module: true}),
     commonjs({
       include: 'node_modules/rxjs/**',
     }),
     uglify()
 ]
}

This file is very simple. Basically, it is telling the compiler that the entry point is “app/main.js” and the final bundle will be created in the “dist” folder and will be called “build.js”.

You can see that I have used some plugins here. Basically, plugins provide optional filters that can be applied to import/export for their transformation (similar to how pipes work!). Here, I used commonjs() and uglify() for allowing the RxJS to use import/export and to minimize the generated code respectively, but I won’t talk about plugins in detail here.

So, now run the Rollup process:

node_modules/.bin/rollup -c rollup-config.js

 

Now, you need to load the bundle like the following (in bottom of your index.html file):

<script src="dist/build.js"></script>

Note: I was getting errors when I did “ng serve” at this moment, I realized angular-cli needed to be updated to the latest version (read: https://github.com/angular/angular-cli/issues/3467), so make sure you update it as well:

 

npm uninstall -g angular-cli
npm cache clean
npm install -g angular-cli@latest

Now, you can “ng serve” to see the results.

Bundling for Production

You can use the following commands to build the final application for the production level:

ng build --prod --aot

This will generate the files in new “dist/” folder. In those files, you can use the “index.html” for opening the website in your browser.

Also, make sure to enable production mode while doing bootstrapping:

 

import {enableProdMode} from '@angular/core';
enableProdMode();

Conclusion

Thus, so far, you can see that several features are provided in AngularJS 2 out-of-the-box that allow you to optimize your application as much as possible. With the Feature Modules at hand to ease the reading of code, the Lazy Loading for loading routes effectively, and the AOT to reduce the compilation time and the total size of the application at your side, you can conquer any battlefield pertaining to web development!

If you have any questions, then please ask in the comment section below!

By Danyal Zia | 2/1/2017 | General

{{CommentsModel.TotalCount}} Comments

Your Comment

{{CommentsModel.Message}}

Recent Stories

Top DiscoverSDK Experts

User photo
3355
Ashton Torrence
Web and Windows developer
GUI | Web and 11 more
View Profile
User photo
3220
Mendy Bennett
Experienced with Ad network & Ad servers.
Mobile | Ad Networks and 1 more
View Profile
User photo
3060
Karen Fitzgerald
7 years in Cross-Platform development.
Mobile | Cross Platform Frameworks
View Profile
Show All
X

Compare Products

Select up to three two products to compare by clicking on the compare icon () of each product.

{{compareToolModel.Error}}

Now comparing:

{{product.ProductName | createSubstring:25}} X
Compare Now