How to bundle an Angular app for production
2 to 11
(TypeScript) with Angular CLI
OneTime Setup
npm install -g @angular/cli
-
ng new projectFolder
creates a new application
Bundling Step
-
ng build --prod
(run in command line when directory isprojectFolder
)flag
prod
bundle for production (see the Angular documentation for the list of option included with the production flag). -
Compress using Brotli compression the resources using the following command
for i in dist/*/*; do brotli $i; done
bundles are generated by default to projectFolder/dist(/$projectFolder
for v6+)**
Output
Sizes with Angular 11.2.12
with CLI 11.2.12
and option CSS without Angular routing
-
dist/main-[es-version].[hash].js
Your application bundled [ ES5 size: 135 KB for new Angular CLI application empty, 38 KB compressed]. -
dist/polyfill-[es-version].[hash].bundle.js
the polyfill dependencies (@angular, RxJS...) bundled [ ES5 size: 36 KB for new Angular CLI application empty, 12 KB compressed]. -
dist/index.html
entry point of your application. -
dist/runtime-[es-version].[hash].bundle.js
webpack loader -
dist/style.[hash].bundle.css
the style definitions -
dist/assets
resources copied from the Angular CLI assets configuration
Deployment
You can get a preview of your application using the ng serve --prod
command that starts a local HTTP server such that the application with production files is accessible using http://localhost:4200. This is not safe to use for production usage.
For a production usage, you have to deploy all the files from the dist
folder in the HTTP server of your choice.
2.0.1 Final
using Gulp (TypeScript - Target: ES5)
OneTime Setup
-
npm install
(run in cmd when direcory is projectFolder)
Bundling Steps
-
npm run bundle
(run in cmd when direcory is projectFolder)bundles are generated to projectFolder / bundles /
Output
-
bundles/dependencies.bundle.js
[ size: ~ 1 MB (as small as possible) ]- contains rxjs and angular dependencies, not the whole frameworks
-
bundles/app.bundle.js
[ size: depends on your project, mine is ~ 0.5 MB ]- contains your project
File Structure
- projectFolder / app / (all components, directives, templates, etc)
- projectFolder / gulpfile.js
var gulp = require('gulp'),
tsc = require('gulp-typescript'),
Builder = require('systemjs-builder'),
inlineNg2Template = require('gulp-inline-ng2-template');
gulp.task('bundle', ['bundle-app', 'bundle-dependencies'], function(){});
gulp.task('inline-templates', function () {
return gulp.src('app/**/*.ts')
.pipe(inlineNg2Template({ useRelativePaths: true, indent: 0, removeLineBreaks: true}))
.pipe(tsc({
"target": "ES5",
"module": "system",
"moduleResolution": "node",
"sourceMap": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"removeComments": true,
"noImplicitAny": false
}))
.pipe(gulp.dest('dist/app'));
});
gulp.task('bundle-app', ['inline-templates'], function() {
// optional constructor options
// sets the baseURL and loads the configuration file
var builder = new Builder('', 'dist-systemjs.config.js');
return builder
.bundle('dist/app/**/* - [@angular/**/*.js] - [rxjs/**/*.js]', 'bundles/app.bundle.js', { minify: true})
.then(function() {
console.log('Build complete');
})
.catch(function(err) {
console.log('Build error');
console.log(err);
});
});
gulp.task('bundle-dependencies', ['inline-templates'], function() {
// optional constructor options
// sets the baseURL and loads the configuration file
var builder = new Builder('', 'dist-systemjs.config.js');
return builder
.bundle('dist/app/**/*.js - [dist/app/**/*.js]', 'bundles/dependencies.bundle.js', { minify: true})
.then(function() {
console.log('Build complete');
})
.catch(function(err) {
console.log('Build error');
console.log(err);
});
});
- projectFolder / package.json (same as Quickstart guide, just shown devDependencies and npm-scripts required to bundle)
{
"name": "angular2-quickstart",
"version": "1.0.0",
"scripts": {
***
"gulp": "gulp",
"rimraf": "rimraf",
"bundle": "gulp bundle",
"postbundle": "rimraf dist"
},
"license": "ISC",
"dependencies": {
***
},
"devDependencies": {
"rimraf": "^2.5.2",
"gulp": "^3.9.1",
"gulp-typescript": "2.13.6",
"gulp-inline-ng2-template": "2.0.1",
"systemjs-builder": "^0.15.16"
}
}
- projectFolder / systemjs.config.js (same as Quickstart guide, not available there anymore)
(function(global) {
// map tells the System loader where to look for things
var map = {
'app': 'app',
'rxjs': 'node_modules/rxjs',
'angular2-in-memory-web-api': 'node_modules/angular2-in-memory-web-api',
'@angular': 'node_modules/@angular'
};
// packages tells the System loader how to load when no filename and/or no extension
var packages = {
'app': { main: 'app/boot.js', defaultExtension: 'js' },
'rxjs': { defaultExtension: 'js' },
'angular2-in-memory-web-api': { defaultExtension: 'js' }
};
var packageNames = [
'@angular/common',
'@angular/compiler',
'@angular/core',
'@angular/forms',
'@angular/http',
'@angular/platform-browser',
'@angular/platform-browser-dynamic',
'@angular/router',
'@angular/router-deprecated',
'@angular/testing',
'@angular/upgrade',
];
// add package entries for angular packages in the form '@angular/common': { main: 'index.js', defaultExtension: 'js' }
packageNames.forEach(function(pkgName) {
packages[pkgName] = { main: 'index.js', defaultExtension: 'js' };
});
var config = {
map: map,
packages: packages
};
// filterSystemConfig - index.asp's chance to modify config before we register it.
if (global.filterSystemConfig) { global.filterSystemConfig(config); }
System.config(config);
})(this);
- projetcFolder / dist-systemjs.config.js (just shown the difference with systemjs.config.json)
var map = {
'app': 'dist/app',
};
-
projectFolder / index.html (production) - The order of the script tags is critical. Placing the
dist-systemjs.config.js
tag after the bundle tags would still allow the program to run but the dependency bundle would be ignored and dependencies would be loaded from thenode_modules
folder.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<base href="/"/>
<title>Angular</title>
<link rel="stylesheet" type="text/css" href="style.css"/>
</head>
<body>
<my-app>
loading...
</my-app>
<!-- Polyfill(s) for older browsers -->
<script src="node_modules/core-js/client/shim.min.js"></script>
<script src="node_modules/zone.js/dist/zone.min.js"></script>
<script src="node_modules/reflect-metadata/Reflect.js"></script>
<script src="node_modules/systemjs/dist/system.js"></script>
<script src="dist-systemjs.config.js"></script>
<!-- Project Bundles. Note that these have to be loaded AFTER the systemjs.config script -->
<script src="bundles/dependencies.bundle.js"></script>
<script src="bundles/app.bundle.js"></script>
<script>
System.import('app/boot').catch(function (err) {
console.error(err);
});
</script>
</body>
</html>
- projectFolder / app / boot.ts is where the bootstrap is.
The best I could do yet :)
Angular 2 with Webpack (without CLI setup)
1- The tutorial by the Angular2 team
The Angular2 team published a tutorial for using Webpack
I created and placed the files from the tutorial in a small GitHub seed project. So you can quickly try the workflow.
Instructions:
npm install
npm start. For development. This will create a virtual "dist" folder that will be livereloaded at your localhost address.
npm run build. For production. "This will create a physical "dist" folder version than can be sent to a webserver. The dist folder is 7.8MB but only 234KB is actually required to load the page in a web browser.
2 - A Webkit starter kit
This Webpack Starter Kit offers some more testing features than the above tutorial and seem quite popular.