Maxim Salnikov
Angular GDE
How to create Angular apps that loaded and executed faster
Products from the future
UI Engineer at ForgeRock
$ ng build --prod
$ ng build --prod --source-map
$ npx source-map-explorer main.bundle.js
const appRoutes: Routes = [{
path: 'tweets',
loadChildren: 'tweets/tweets.module#TweetsModule'
}]
const tweetsRoutes: Routes = [{
path: '',
component: TweetFeedsComponent
}];
@NgModule({
imports: [RouterModule.forRoot(appRoutes, {
preloadingStrategy: PreloadAllModules
})],
exports: [RouterModule]
})
$ ng add @angular/pwa
$ ng build --prod
App
Browser
App
Service worker
Cache
Browser
{
"assetGroups": [
Smart defaults for the app shell
],
"dataGroups": [
Settings for your API, etc
],
...
}
{
"name": "myApi",
"urls": [
"/api/archive/**"
],
}
"cacheConfig": {
"strategy": "performance",
"maxSize": 100,
"maxAge": "365d"
}
First meaningful paint
First meaningful paint
No SSR
SSR
<app-root>Loading</app-root>
Loading
$ ng add @ng-toolkit/universal
$ npm run build:prod && npm run server
$ ng add @nguniversal/express-engine
$ npm run build:ssr && npm run serve:ssr
Change detection
UI render
Trigger
Ready
<span>{{ relativeDate(tweet.createdAt) }}</span>
<span>{{ tweet.createdAt | relativeDate }}</span>
import { Pipe, PipeTransform } from '@angular/core';
@Pipe({
name: 'relativeDate',
pure: true
})
export class RelativeDatePipe implements PipeTransform {
transform(date) {
// Transformations...
return newDate;
}
}
Memoization is an optimization technique used primarily to speed up computer programs by storing the results of expensive function calls and returning the cached result when the same inputs occur again
import { memoize } from 'lodash-decorators';
...
@memoize()
relativeDate(date) {
// Calculations
return newDate;
}
By default
By default
By default
By default
import { ..., ChangeDetectionStrategy } from '@angular/core';
@Component({
changeDetection: ChangeDetectionStrategy.OnPush
})
import { ..., ChangeDetectorRef } from '@angular/core';
class RealTimeList {
constructor(private ref: ChangeDetectorRef) {
ref.detach();
setInterval(() => {
this.ref.detectChanges();
}, 1000);
}
}
import { ..., NgZone } from '@angular/core';
class MyComponent implements OnChanges {
constructor(private _ngZone: NgZone ) {}
log() {
this._ngZone.runOutsideAngular(() => {
// Any code will not cause CD
}}));
}
}
export class TweetListComponent implements OnInit {
...
trackById(index, tweet) {
return tweet.id;
}
}
<div *ngFor="let tweet of tweets; trackBy: trackById">