DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports Events Over 2 million developers have joined DZone. Join Today! Thanks for visiting DZone today,
Edit Profile Manage Email Subscriptions Moderation Admin Console How to Post to DZone Article Submission Guidelines
View Profile
Sign Out
Refcards
Trend Reports
Events
Zones
Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones AWS Cloud
by AWS Developer Relations
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones
AWS Cloud
by AWS Developer Relations
  1. DZone
  2. Coding
  3. JavaScript
  4. AOT Compilation With Bundling in Angular

AOT Compilation With Bundling in Angular

We discuss the differences between JIT and AOT compilation before demonstrating how to build AOT into your Angular-based application using Webpack.

Debasis Saha user avatar by
Debasis Saha
CORE ·
Aug. 14, 18 · Tutorial
Like (2)
Save
Tweet
Share
13.89K Views

Join the DZone community and get the full member experience.

Join For Free

In the world of web development, developers enjoy developing SPAs (Single Page Applications) using Angular. One of the main reasons behind this is the modular approach of Angular, with the help of which we can separate our client-side scripting codes, like MVC patterns, from the rest of the codebase. But, the most problematic issue is that adding all the JavaScript files into our index.html page is really very painful. Not only that, it decreases the performance of the application since, while loading the index.html file, it tries to download all the JavaScript files, including related HTML files. So, if we assume that our application contains more than 500 components, then we can clearly think of how many files need to download at the beginning. Not only the files related to the components which we developed for the application, but index files download the environmental files related to Angular from the Node Modules. So, in this way, it increases the application network traffic and decreases the performance. So, the most preferred solution is that if we can bundle all the files into a single file, just like other JavaScript-based applications or MVC applications. So, to achieve this, we need to change the compilation process of our Angular applications.

JIT (Just In Time) Compilation

Traditional Angular applications (i.e. developed in Angular 1.x) were normally built using the JIT compilation. Maybe, the developers who mainly code using client-side languages/libraries like JavaScript/jQuery do not know about it or believe that there is no compile-time requirement in the case of script language. But, this is not true. Actually, in the case of JavaScript, there is always a compiler which compiles our code. This does not occur in the development or deployment time. Basically, it occurs on the fly, meaning just before our JavaScript code is going to be loaded by the browser. This process always takes a little bit more time than we think. This compilation process is required for the browsers because the browser does not read or understand any binary languages to execute or run. So, when we load any JavaScript files, those files must first be compiled so the browser can understand them and then execute them. So, AOT compilation process actually switches the compilation process from runtime to build time.

Now, we said that browsers do not understand binary languages. The browser always needs a JS file to execute. So, the question is, what is compiled at the build time by the AOT Compiler?

AOT (Ahead of Time) Compilation

AOT stands for Ahead of Time Compilation. AOT compilation was one the most important features introduced in Angular 2 and above versions. The process mechanism is very different in the case of AOT compilation versus JIT compilation. It was first introducing in Angular 2.0, but is now only available in version 2.3.0 and above. So, if anyone wants to use this compilation process, they need to use Angular 2.3.0 or above. Now, the main objective of AOT compilation is improving the performance of the application when it runs in the browser. Actually, the Angular framework has a compiler library package known as @angular/compiler. If we compile the Angular application with the AOT mode, it performs the compilation of templates with the help of the compiler and then generates the TypeScript files (file names like *ngfactory.ts, where the * represents the actual name of the file; for example, if the component name is employee.ts then the related file will be employee.ngfactory.ts). Then the compiler again compiles these TypeScript files to the JavaScript files.

In this compilation process, a new type of file (*.metadata.json) has been created for every TypeScript file. This metadata file is required to compile the components for their templates. So, the AOT compiler validates the HTML templates at build time and also raises an error if it finds any issues in our code. The most common issues are:

  • Properties and methods which are used within the HTML template must be declared as public in the TypeScript class.
  • In case of *ngIf directives, expressions must be Boolean.
  • In case of event binding, method signatures must be matched exactly with the TypeScript class method declarations.

WebPack

After the concept of AOT compilation, we also need to bundle our all .js files into one JavaScript file so that our application’s index page does not need to load a bunch of different files. For bundling the compiled JavaScript files, we have different tools available to us. One of the most important tools is Webpack. Webpack has a few advantages over the other tools, such:

  • Webpack can provide us a module loading concept, just like Node.js can.
  • The bundle file is easy to read and debug in the browser.
  • Webpack can be configured by writing a config file code in ES2015.
  • Webpack provides a similar development environment to the production/deployment enviroment.
  • With the help of Webpack, we can install Angular using NPM (Node Package Manager).

Application Configuration

For using the bundle concept, we need to develop an Angular-based application that contains modules, components, etc. The module structure is given below:

Image title

In the above image, we can see that there are two config file that exist, namely webpack.config.js and webpack.prod-config.js. These two files are basically used as the webpack configuration files, which are required to build the application either in development mode or production mode. Before going into configuration file code, we first need to install Webpack using  the following npm command:

npm install angular webpack --save-dev 

Now, we can add two webpack config files. Basically, we can bundle our Angular application either using JIT compilation or AOT compilation. The config file webpack.config.js is used for bundle the application with JIT compilation. To bundle the application, in this mode, we need to use the below command from the applications root directory:

npm run build 

Sample Code for webpack.config.js

'use strict'
const path = require('path');
const ContextReplacementPlugin = require('webpack/lib/ContextReplacementPlugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const StatsPlugin = require('stats-webpack-plugin');
const TsConfigPathsPlugin = require('awesome-typescript-loader').TsConfigPathsPlugin;
const webpack = require('webpack');

const testHTML = path.resolve(__dirname, 'submodules', 'index.html');

module.exports = {
  devtool: '',
  profile: true,

  entry: {
    'shared/polyfills': [
      './submodules/app1/polyfills.ts',
      './submodules/app2/polyfills.ts',
      './submodules/app3/polyfills.ts'
    ],
    'app1/firstapp': ['./submodules/app1/index.ts'],
    'app2/secondapp': ['./submodules/app2/index.ts'],
    'app3/thirdapp': ['./submodules/app3/index.ts']
  },

  output: {
    path: path.join(__dirname, 'dist', 'dev'),
    publicPath: '/',
    filename: '[name].js',
    chunkFilename: '[name].js'
  },

  resolve: {
    extensions: ['.ts', '.js', '.json'],
    modules: [
      path.resolve(__dirname, 'node_modules'),
      path.resolve(__dirname, 'build-cache')
    ]
  },

  module: {
    rules: [
      {
        test: /\.ts$/,
        use: [
          'awesome-typescript-loader',
          'angular2-template-loader'
        ],
        exclude: [/\.(spec|e2e)\.ts$/]
      },
      {
        test: /\.html$/,
        use: 'raw-loader'
      },
      {
        test: /\.css$/,
        use: [
          'to-string-loader',
          'css-loader'
        ]
      },
    ]
  },

  plugins: [
    new StatsPlugin('stats.json', 'verbose'),

    new webpack.optimize.OccurrenceOrderPlugin(),

    new webpack.optimize.CommonsChunkPlugin({
      name: 'shared/polyfills',
      chunks: ['shared/polyfills']
    }),
    new webpack.optimize.CommonsChunkPlugin({
      name: 'shared/vendors',
      chunks: [
        'app1/firstapp',
        'app2/secondapp',
        'app3/thirdapp'
      ],
      // Move all node_modules into vendors chunk but not @angular
      minChunks: module => /node_modules\/(?!@angular)/.test(module.resource)
    }),
    new webpack.optimize.CommonsChunkPlugin({
      name: 'shared/angular-vendors',
      chunks: [
        'app1/firstapp',
        'app2/secondapp',
        'app3/thirdapp'
      ],
      // Move all node_modules into vendors chunk but not @angular
      minChunks: module => /node_modules\/@angular/.test(module.resource)
    }),
    new webpack.optimize.CommonsChunkPlugin({
      name: 'shared/shared-modules',
      chunks: [
        'app1/firstapp',
        'app2/secondapp',
        'app3/thirdapp'
      ],
      // Move duplicate modules to a shared-modules chunk
      minChunks: 2
    }),
    // Specify the correct order the scripts will be injected in
    new webpack.optimize.CommonsChunkPlugin({
      name: [
        'shared/polyfills',
        'shared/vendors',
        'shared/angular-vendors'
      ].reverse()
    }),

    new HtmlWebpackPlugin({
      template: testHTML,
      inject: false
    })
  ],

  node: {
    global: true,
    crypto: 'empty',
    process: true,
    module: false,
    clearImmediate: false,
    setImmediate: false
  },
  target: 'web'
}

Another config file, i.e. webpack.prod-config.js, is required for bundling the applications with AOT compilation. As we all known, every Angular application needs a module bootstrap file, where we can bootstrap the Angular module. Normally, this file is named main.ts or index.ts. In the above code, we can see that every Angular app module folder (like app1, app2, etc.) contains another file named index.aot.tswhich is basically required to bootstrap the Angular module in the AOT compilation mode. In this file, we bootstrapped the Angular module's ngfactoryfile which we created after the AOT compilations. To bundle the application, in this mode, we need to use the below command from the application's root directory:

npm run build:prod

Sample Code of Index.aot.ts

import { enableProdMode } from '@angular/core';
import { platformBrowser } from '@angular/platform-browser';
import { AppModuleNgFactory } from 'submodules/app1/app.module.ngfactory';

try {
  enableProdMode();
} catch (err) {
  /**
   * We do not care if the call to enableProdMode() failes
   * because we only need one of the calls to enableProdMode()
   * to succeed.
   */
  if (err.message.indexOf('Cannot enable prod mode after platform setup.') === -1)
    console.error(err);
}

platformBrowser()
  .bootstrapModuleFactory(AppModuleNgFactory);

Sample Code of webpack.prod-config.ts

'use strict'
const path = require('path');
const ContextReplacementPlugin = require('webpack/lib/ContextReplacementPlugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const StatsPlugin = require('stats-webpack-plugin');
const TsConfigPathsPlugin = require('awesome-typescript-loader').TsConfigPathsPlugin;
const webpack = require('webpack');

const testHTML = path.resolve(__dirname, 'submodules', 'index.html');

module.exports = {
  devtool: '',
  profile: true,

  entry: {
    'shared/polyfills': [
      './submodules/app1/polyfills.ts',
      './submodules/app2/polyfills.ts',
      './submodules/app3/polyfills.ts'
    ],
    'app1/app': ['./submodules/app1/index.aot.ts'],
    'app2/app': ['./submodules/app2/index.aot.ts'],
    'app3/app': ['./submodules/app3/index.aot.ts']
  },

  output: {
    path: path.join(__dirname, 'dist', 'prod'),
    publicPath: '/',
    filename: '[name].js',
    chunkFilename: '[name].js'
  },

  resolve: {
    extensions: ['.ts', '.js', '.json'],
    modules: [
      path.resolve(__dirname, 'node_modules'),
      path.resolve(__dirname, 'build-cache'),
    ]
  },

  module: {
    rules: [
      {
        test: /\.ts$/,
        use: [
          'awesome-typescript-loader',
          'angular2-template-loader'
        ],
        exclude: [/\.(spec|e2e)\.ts$/]
      },
      {
        test: /\.html$/,
        use: 'raw-loader'
      },
      {
        test: /\.css$/,
        use: [
          'to-string-loader',
          'css-loader'
        ]
      },
    ]
  },

  plugins: [
    new StatsPlugin('stats.json', 'verbose'),

    new webpack.optimize.OccurrenceOrderPlugin(),
    new webpack.optimize.UglifyJsPlugin({
      beautify: false,
      output: {
        comments: false
      },
      mangle: {
        screw_ie8: true,
        except: ['$']
      },
      compress: {
        screw_ie8: true,
        warnings: false,
        collapse_vars: true,
        reduce_vars: true,
        conditionals: true,
        unused: true,
        comparisons: true,
        sequences: true,
        dead_code: true,
        evaluate: true,
        if_return: true,
        join_vars: true,
        drop_console: false,
        drop_debugger: true
      }
    }),

    new webpack.optimize.CommonsChunkPlugin({
      name: 'shared/polyfills',
      chunks: ['shared/polyfills']
    }),
    new webpack.optimize.CommonsChunkPlugin({
      name: 'shared/vendors',
      chunks: [
        'app1/app',
        'app2/app',
        'app3/app'
      ],
      // Move all node_modules into vendors chunk but not @angular
      minChunks: module => /node_modules\/(?!@angular)/.test(module.resource)
    }),
    new webpack.optimize.CommonsChunkPlugin({
      name: 'shared/angular-vendors',
      chunks: [
        'app1/app',
        'app2/app',
        'app3/app'
      ],
      // Move all node_modules into vendors chunk but not @angular
      minChunks: module => /node_modules\/@angular/.test(module.resource)
    }),
    new webpack.optimize.CommonsChunkPlugin({
      name: 'shared/shared-modules',
      chunks: [
        'app1/app',
        'app2/app',
        'app3/app'
      ],
      // Move duplicate modules to a shared-modules chunk
      minChunks: 2
    }),
    // Specify the correct order the scripts will be injected in
    new webpack.optimize.CommonsChunkPlugin({
      name: [
        'shared/polyfills',
        'shared/vendors',
        'shared/angular-vendors'
      ].reverse()
    }),

    new HtmlWebpackPlugin({
      template: testHTML,
      inject: false
    })
  ],

  node: {
    global: true,
    crypto: 'empty',
    process: true,
    module: false,
    clearImmediate: false,
    setImmediate: false
  },
  target: 'web'
}

After running any one  of the commands given above, two new folders named build-cache and dist (these two folders are mentioned in the webpack config files) have been created in the root application folder directory, as shown below.

Image title

The dist folder is the output folder where the final files have been stored after compilation. If we use JIT compilation, then the output files will be stored under the dev folder, where the same will be stored under the prod folder for the AOT compilations. After taht, we just need to deploy the contents of these folders (either dev or prod) to the server as an application. Also, note that this compiled folder does not contains any node_modules folder because it has already bundled the related content of the required node module package files within the shared-modules.js and polyfill.js files.

Image title

You can download the complete source code from the link - Angular AOT Compilation

AngularJS application JavaScript

Opinions expressed by DZone contributors are their own.

Popular on DZone

  • HTTP vs Messaging for Microservices Communications
  • Monolithic First
  • Key Elements of Site Reliability Engineering (SRE)
  • How To Build a Spring Boot GraalVM Image

Comments

Partner Resources

X

ABOUT US

  • About DZone
  • Send feedback
  • Careers
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 600 Park Offices Drive
  • Suite 300
  • Durham, NC 27709
  • support@dzone.com
  • +1 (919) 678-0300

Let's be friends: