Top Banner
Small computers, big performance: Optimize your Angular
39

Angular Optimization Web Performance Meetup

Apr 13, 2017

Download

Software

David Barreto
Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
Page 1: Angular Optimization Web Performance Meetup

Small computers, big performance:Optimize your Angular

Page 2: Angular Optimization Web Performance Meetup

SpeakersDavid Barreto

Solutions Architect @ Rangle.io

Blog: david-barreto.com

Andrew Smith

Solutions Architect @ Rangle.io

Page 3: Angular Optimization Web Performance Meetup

Agenda

1. Ahead of Time Compilation2. Lazy Loading3. Change Detection4. Memory Leaks5. Server Side Rendering

Page 4: Angular Optimization Web Performance Meetup

Rangle Academy

Goal and Structure:

Program to share knowledge within the companyIt follows a "workshop" structureUsually 2 hours longCovers hard and soft skills

Some workshops available:

WebpackReactReact NativeGoogle AnalyticsUnit TestingIntroduction to Payment GatewaysContinuous Delivery to ProductionConflict Management

Page 5: Angular Optimization Web Performance Meetup

About the Demo App

Characteristics:

Built using Angular 2.4.1

Uses Angular CLI beta-26

Redux store with ngrx

Tachyons for CSS

Server side rendering with Angular Universal

All the numbers shown are based on:

Low end device (5x CPU slowdown)

Good 3G connection (Latency: 40ms, DL: 1.5 Mbps, UL: 750 Kbps)

Page 6: Angular Optimization Web Performance Meetup

Ahead of Time Compilation (AoT)

Page 7: Angular Optimization Web Performance Meetup

Compilation Modes

Just in Time Compilation (JiT):

Compilation performed in the browser at run timeBigger bundle size (includes the compiler)Takes longer to boot the app

$ ng serve --prod

Ahead of Time Compilation (AoT):

Compilation performed in the server at build timeSmaller bundle size (doesn't include the compiler)The app boots faster

$ ng serve --prod --aot

Page 8: Angular Optimization Web Performance Meetup
Page 9: Angular Optimization Web Performance Meetup
Page 10: Angular Optimization Web Performance Meetup

JiT vs AoT in Demo App (Prod + Gzip)

CSS files are included in the "other js files"

File Size (JiT) Size (AoT)

main.bundle.js 6.4 KB 23.9 KB

vendor.bundle.js 255 KB 158 KB

other js files 48.7 KB 49.6 KB

Total Download 306 KB 231.5 KB

AoT goals (from the ):docs

Faster rendering => Components already compiledFewer async request => Inline external HTML and CSSSmaller bundle size => No compiler shippedDetect template errors => Because they canBetter security => Prevents script injection attack

Page 11: Angular Optimization Web Performance Meetup

Boot Time Comparison

Event Time (JiT) Time (AoT)

DOM Content Loaded 5.44 s 3.25 s

Load 5.46 s 3.27 s

FMP 5.49 s 3.30 s

DOM Content Loaded:

The browser has finished parsing the DOMjQuery nostalgia => $(document).ready()

Load: All the assets has been downloaded

First Meaningful Paint (FMP):

When the user is able to see the app "live" for the first time

(Show browser profile for both modes)

Page 12: Angular Optimization Web Performance Meetup

Lazy Loading

Page 13: Angular Optimization Web Performance Meetup

What is Lazy Loading?

Ability to load modules on demand => Useful to reduce the app startup time

(Compare branches no-lazy-loading vs normal-lazy-loading )

Page 14: Angular Optimization Web Performance Meetup

Bundle Sizes Comparison (Prod + AoT)

File Size (No LL) Size (LL)

main.bundle.js 23.9 KB 17.4 KB

vendor.bundle.js 158 KB 158 KB

other js files 49.6 KB 49.6 KB

Initial Download 231.5 KB 225 KB

0.chunk.js - 9.1 KB

Total Download 231.5 KB 234.1 KB

Webpack creates a "chunk" for every lazy loaded moduleThe file 0.chunk.js is loaded when the user navigates to adminThe initial download size is smaller with LLThe total size over time is bigger with LL because of Webpack async loadingThe effect of LL start to be noticeable when the app grows

Page 15: Angular Optimization Web Performance Meetup

Boot Time Comparison (Prod + AoT)

Event Time (No LL) Time (LL)

DOM Content Loaded 3.25 s 3.11 s

Load 3.27 s 3.25 s

FMP 3.30 s 3.16 s

Not much difference for an small appJust one lazy loaded module with a couple of componentsThe impact is noticeable for big apps

Page 16: Angular Optimization Web Performance Meetup

Preloading

(Compare branches normal-lazy-loading vs master )

Page 17: Angular Optimization Web Performance Meetup

Enable Preloading

Define the property preloadingStrategy in the root module routing

import { PreloadAllModules } from '@angular/router'; export const routes: Routes = [ ... ]; @NgModule({ imports: [ RouterModule.forRoot(routes, { preloadingStrategy: PreloadAllModules }) ], exports: [ RouterModule ], }) export class AppRoutingModule {}

Page 18: Angular Optimization Web Performance Meetup

Change Detection

Page 19: Angular Optimization Web Performance Meetup

What's Change Detection (CD)?

It's a mechanism to keep our "models" in sync with our "views"

Change detection is fired when...

The user interacts with the app (click, submit, etc.)An async event is completed (setTimeout, promise, observable)

When CD is fired, Angular will check every component starting from the top once.

Page 20: Angular Optimization Web Performance Meetup

Change Detection Strategy: OnPush

Angular offers 2 strategies:

Default: Check the entire component when CD is firedOnPush: Check only relevant subtrees when CD is fired

OnPush Requirements:

Component inputs ( @Input ) need to be immutable objects

@Component({ selector: 'rio-workshop', templateUrl: './workshop.component.html', changeDetection: ChangeDetectionStrategy.OnPush }) export class WorkshopComponent { @Input() workshop: Workshop; @Input() isSummary = false; }

View Example

Page 21: Angular Optimization Web Performance Meetup

Example: The Component Tree

Page 22: Angular Optimization Web Performance Meetup

Default Change Detection

Page 23: Angular Optimization Web Performance Meetup

OnPush Change Detection

Page 24: Angular Optimization Web Performance Meetup

Summary

What to do?

Apply the OnPush change detection on every component*Never mutate an object or array, always create a a new reference ( )blog

// Don't let addPerson = (person: Person): void => { people.push(person); }; // Do let addPerson = (people: Person[], person: Person): Person[] => { return [ ...people, person ]; };

Benefits:

Fewer checks of your components during Change DetectionImproved overall app performance

Page 25: Angular Optimization Web Performance Meetup

Memory Leaks

Page 26: Angular Optimization Web Performance Meetup

What's Memory Leak?

The increase of memory usage over time

Page 27: Angular Optimization Web Performance Meetup

What Causes Memory Leaks in Angular?

Main Source => Subscriptions to observables never closed

@Injectable() export class WorkshopService { getAll(): Observable<Workshop[]> { ... } }

@Component({ selector: 'rio-workshop-list', template: ` <div *ngFor="let workshop of workshops"> {{ workshop.title }} </div>` }) export class WorkshopListComponent implements OnInit { ... ngOnInit() { this.service.getAll().subscribe(workshops => this.workshops = workshops); } }

Page 28: Angular Optimization Web Performance Meetup

Manually Closing Connections

Before the element is destroyed, close the connection

@Component({ selector: 'rio-workshop-list', template: ` <div *ngFor="let workshop of workshops"> {{ workshop.title }} </div>` }) export class WorkshopListComponent implements OnInit, OnDestroy { ... ngOnInit() { this.subscription = this.service.getAll() .subscribe(workshops => this.workshops = workshops); } ngOnDestroy() { this.subscription.unsubscribe(); } }

Page 29: Angular Optimization Web Performance Meetup

The async Pipe

It closes the connection automatically when the component is destroyed

@Component({ selector: 'rio-workshop-list', template: ` <div *ngFor="let workshop of workshops$ | async"> {{ workshop.title }} </div>` }) export class WorkshopListComponent implements OnInit { ngOnInit() { this.workshops$ = this.service.getAll(); } }

This is the recommended way of dealing with Observables in your template!

Page 30: Angular Optimization Web Performance Meetup

The Http Service

Every method of the http services ( get , post , etc.) returns an observableThose observables emit only one value and the connection is closed automaticallyThey won't cause memory leak issues

@Component({ selector: 'rio-workshop-list', template: ` <div *ngFor="let workshop of workshops"> {{ workshop.title }} </div>` }) export class WorkshopListComponent implements OnInit { ... ngOnInit() { this.http.get('some-url') .map(data => data.json()) .subscribe(workshops => this.workshops = workshops); } }

Page 31: Angular Optimization Web Performance Meetup

Emit a Limited Number of Values

RxJs provides operators to close the connection automaticallyExamples: first() and take(n)

This won't cause memory leak issues even if getAll emits multiple values

@Component({ selector: 'rio-workshop-list', template: ` <div *ngFor="let workshop of workshops"> {{ workshop.title }} </div>` }) export class WorkshopListComponent implements OnInit { ngOnInit() { this.service.getAll().first() .subscribe(workshops => this.workshops = workshops); } }

Page 32: Angular Optimization Web Performance Meetup

Server Side Rendering

Page 33: Angular Optimization Web Performance Meetup

Angular Universal

Provides the ability to pre-render your application on the server

Much faster time to first paint

Enables better SEO

Enables content preview on social networks

Fallback support for older browsers

Use the as the base of your applicationuniversal-starter

Page 34: Angular Optimization Web Performance Meetup

What's Included

Suite of polyfills for the server

Server rendering layer

Preboot - replays your user's interactions after Angular has bootstrapped

State Rehydration - Don't lose your place when the application loads

Page 35: Angular Optimization Web Performance Meetup

Boot Time Comparison (Client vs Server)

Both environments include previous AoT and Lazy Loading enhancements

Event Time (Client) Time (Server)

DOM Content Loaded 3.11 s 411 ms

Load 3.25 s 2.88 s

FMP 3.16 s ~440 ms

*Times are on mobile over 3G

Page 36: Angular Optimization Web Performance Meetup

Universal Caveats

Cannot directly access the DOM

constructor(element: ElementRef, renderer: Renderer) { renderer.setElementStyle(element.nativeElement, ‘font-size’, ‘x-large’); }

Current solutions only cover Express and ASP.NET servers

Project will be migrated into the core Angular repo for v4

Page 37: Angular Optimization Web Performance Meetup

Summary

Page 38: Angular Optimization Web Performance Meetup

Performance Changes

Event JiT AoT Lazy Loading SSR

DOM Content Loaded 5.44 s 3.25 s 3.11 s 411 ms

Load 5.46 s 3.27 s 3.25 s 2.88 s

FMP 5.46 s 3.30 s 3.16 s ~440 ms

% Improvement (FMP) 39.6% 4.3% 86.1%

*Times are on mobile over 3G

Page 39: Angular Optimization Web Performance Meetup

Thank You

Slides: https://github.com/rangle/angular-performance-meetup