From Angular CLI to Webpack 2 and other production solutions (with examples)
I’m assuming that you have an Angular project (version 2 or 4) generated with Angular CLI 1.0 or higher.
See the full project you may here — https://github.com/sani-banani/angular4-starter
Reasons to migrate from Angular CLI to Webpack:
- More configurable (loaders, plugins,…).
- And more flexibility for various configurations (local, development, production).
0. Pre-steps:
0.1. Generating and serving an Angular project with Angular CLI (version 1.2.6).
ng new angular4-starter
cd angular4-starter
ng serve
More Angular CLI instructions is here.
0.2. Add some changes for CSS and add fonts and images.
ng build -prod

1. Extract webpack file
Since Angular CLI v1.0, there’s the “eject” feature, that allows you to extract the webpack config file and manipulate it as you wish.
- Run
ng eject
so Angular CLI generates the webpack.config.js file. - Run
npm install
so the new dependencies generated by CLI are satisfied
So, we have "ejected": true
in .angular-cli.json, new file — webpack.config.js, and modified run scripts in package.json.
2. Transform result ugly webpack config
Refactor ugly config and make configs for local, development and production with WebpackMerge.


If we add "ejected": false
to .angular-cli.json we can also use the ng command.

What we see? We have more smaller assets size with webpack than with command ng build -prod
.
3. Other production solutions:
3.1. Node JS Server
If we use compression with node js, we have more smaller assets:


server.js
const compression = require('compression');
const express = require('express');
const app = express();
app.set('port', (process.env.PORT || 8081));
// Gzip
app.use(compression());
// Run the app by serving the static files
// in the dist directory
app.use('/', express.static(__dirname + '/dist'));
// Start the app by listening on the default port
app.listen(app.get('port'), function() {
console.log('Angular4 Starter App listening on port ' + app.get('port'));
});
3.2. Lazy Module
Reasons: For best loading https://github.com/mgechev/angular-performance-checklist#lazy-loading-of-resources
File structure of administration module is below
administration-routing.module.ts
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { AdministrationComponent } from './administration.component';
import { UserListComponent } from './user-list/user-list.component';
import { UserInfoComponent } from './user-info/user-info.component';const routes: Routes = [
{ path: '', component: AdministrationComponent }, children: [
{ path: 'user-list/:page', component: UserListComponent },
{ path: 'user-list', redirectTo: 'user-list/0' },
{ path: 'user-info/:id', component: UserInfoComponent },
{ path: '', redirectTo: 'user-list/0' },
] }
];@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class AdministrationRoutingModule { }
app-routing.module.ts
{ path: 'administration', loadChildren: './+administration/administration.module#AdministrationModule' },
3.2. Shared Module for AppModule & LazyModule
Module with shared services and components:
import { NgModule } from '@angular/core';
import { CommonModule } from "@angular/common";
import { FormsModule } from '@angular/forms';
import { MyDatePickerModule } from 'mydatepicker';
import { SelectModule } from 'angular2-select';import { Tab, Tabs } from '../tabs/tabs';
import { Countries } from './dictionary/countries';@NgModule({
imports: [
CommonModule,
FormsModule,
MyDatePickerModule,
SelectModule,
],
exports: [
CommonModule,
FormsModule,
MyDatePickerModule,
SelectModule,
Tab, Tabs
],
declarations: [
Tab, Tabs
],
providers: [ Countries ],
})
export class SharedModule {}
app.module.ts
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppRoutingModule } from './app-routing.module';
import { HttpModule } from '@angular/http';
import { LightboxModule } from 'angular2-lightbox';
import { SharedModule } from './shared/shared.module';import { BackendService } from './shared/service/backend.service';
import { ApiService } from './shared/service/api.service';import { AppComponent } from './app.component';
import { PageNotFoundComponent } from './page-not-found/page-not-found.component';
import { RegistrationComponent } from './registration/registration.component';
import { HomeComponent } from './home/home.component';
import { SuccessComponent } from './success/success.component';
import { FilesComponent } from './files/files.component';
import { BreadcrumbComponent } from './breadcrumb/breadcrumb.component';
import { ParticipantsComponent } from './participants/participants.component';
import { VisaComponent } from './visa/visa.component';
import { ProgramComponent } from './program/program.component';
import { FoodComponent } from './food/food.component';
import { PlaceComponent } from './place/place.component';
import { ContactsComponent } from './contacts/contacts.component';
import { DirectionsComponent } from './directions/directions.component';
import { CultureComponent } from './culture/culture.component';
import { UnderConstructionComponent } from './under-construction/under-construction.component';@NgModule({
declarations: [
AppComponent,
PageNotFoundComponent,
RegistrationComponent,
HomeComponent,
SuccessComponent,
FilesComponent,
BreadcrumbComponent,
ParticipantsComponent,
VisaComponent,
ProgramComponent,
FoodComponent,
PlaceComponent,
ContactsComponent,
DirectionsComponent,
CultureComponent,
UnderConstructionComponent
],
imports: [
AppRoutingModule,
BrowserModule,
HttpModule,
LightboxModule,
SharedModule,
],
providers: [ ApiService, BackendService ],
bootstrap: [ AppComponent ]
})
export class AppModule { }
administration.module.ts
import { NgModule } from '@angular/core';
import { SharedModule } from '../shared/shared.module';import { AdministrationRoutingModule } from './administration-routing.module';
import { AdministrationComponent } from './administration.component';
import { UserListComponent } from './user-list/user-list.component';
import { UserInfoComponent } from './user-info/user-info.component';@NgModule({
imports: [
AdministrationRoutingModule,
SharedModule,
],
exports: [],
declarations: [
AdministrationComponent,
UserInfoComponent,
UserListComponent,
],
providers: [],
})
export class AdministrationModule { }
3.3. Static and Node.js supporting
1) Use hash-urls (#) routing both for static and server mode
@NgModule({
imports: [ RouterModule.forRoot(routes, { useHash: true }) ],
exports: [ RouterModule ]
})
2) Use <base href=”./”>
3.4. Proxy
It is simple to avoid cors problems for local development. Let see examples below for angular-cli and webpack.
1) Angular CLI: Create proxy.conf.json for proxying requests
{
"/backend": {
"target": "http://amazon.com",
"secure": false
}
}
And start it:
ng serve --environment local --proxy-config proxy.conf.json --host 0.0.0.0 --port 8080
2) Webpack: Change webpack.local.js config for local development
const proxyConf = require('../proxy.conf.json');devServer: {
historyApiFallback: true,
stats: 'minimal',
proxy: proxyConf
}