Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Angular-Performance: So zünden Sie den Turbo

Angular-Performance: So zünden Sie den Turbo

Was in zwei Sekunden nicht lädt, wird nie wieder aufgerufen: Die Erwartung von Anwendern an die Performance von Webanwendungen nimmt stetig zu. Und auch nach dem Laden einer Webanwendung muss sich diese schnell und flüssig anfühlen, damit der Anwender sein Ziel erreichen kann. Mit Zones, Change-Detection- und Preloading-Strategien, Lazy Loading, Ahead-of-time-Kompilierung und Service Workern stehen in Angular verschiedene Techniken zur Verfügung, um selbst große Webanwendungen superschnell auszuführen. Christian Liebel von Thinktecture zeigt Ihnen die Stellschrauben des Frameworks, damit Sie auch für Ihre Angular-App den Turbo zünden können.

Christian Liebel
PRO

October 10, 2019
Tweet

More Decks by Christian Liebel

Other Decks in Programming

Transcript

  1. Angular Performance
    So zünden Sie den Turbo
    Christian Liebel
    @christianliebel
    Consultant

    View Slide

  2. What you cannot expect
    - Web performance in general
    (e.g. HTTP/2, compression, JS
    tweaks, CSS)
    - Deep-dive into performance
    metrics
    What you can expect
    - Angular-specific performance
    measures
    - Common performance pitfalls
    and how to avoid them
    - Hands-on exercies for
    everyone
    Angular Performance
    So zünden Sie den Turbo
    Expectations

    View Slide

  3. Hello, it’s me.
    Angular Performance
    So zünden Sie den Turbo
    Christian Liebel
    Follow me:
    @christianliebel
    Blog:
    christianliebel.com
    Email:
    christian.liebel
    @thinktecture.com
    Cross-Platform &
    Serverless

    View Slide

  4. Angular Performance
    So zünden Sie den Turbo
    Timetable
    13:30–15:00 Part I
    15:00–15:30 Coffee break
    15:30–17:00 Part II

    View Slide

  5. Requirements for hands-on labs:
    Node.js + Angular CLI + Google Chrome + Editor
    Repo: https://liebel.io/ngp19h
    Angular Performance
    So zünden Sie den Turbo

    View Slide

  6. 53% der Benutzer springen ab, wenn eine Website
    länger lädt als 3 Sekunden. (DoubleClick, Google)
    Für jede Sekunde Ladezeit springen
    je 10% der Benutzer ab. (BBC)
    Bessere Performance = geringere Absprungrate
    Angular Performance
    So zünden Sie den Turbo

    View Slide

  7. Runtime Performance
    Bundling
    Lazy
    Loading
    Preloading
    Strategies
    Server-Side
    Rendering
    Service
    Worker
    Angular Performance
    So zünden Sie den Turbo
    Agenda
    Change
    Detection
    Basics
    Zone.js &
    NgZone
    Change
    Detection
    Strategies
    Change
    Detector
    Async Pipe
    Load Time Performance

    View Slide

  8. Don’t over-optimize.
    Angular Performance
    So zünden Sie den Turbo
    First Rule

    View Slide

  9. Reduce required computations during runtime
    - calculations
    - painting
    - layouting
    - …
    Today: Angular-specific performance topics
    Angular Performance
    So zünden Sie den Turbo
    Runtime Performance

    View Slide

  10. Runtime Performance
    Bundling
    Lazy
    Loading
    Preloading
    Strategies
    Server-Side
    Rendering
    Service
    Worker
    Angular Performance
    So zünden Sie den Turbo
    Agenda
    Change
    Detection
    Basics
    Zone.js &
    NgZone
    Change
    Detection
    Strategies
    Change
    Detector
    Async Pipe
    Load Time Performance

    View Slide

  11. Hi Angular!
    Basics
    // app.component.html
    Hi {{ title }}!
    // app.component.ts
    @Component({ /* … */ })
    export class AppComponent {
    title = 'Angular';
    }
    Angular Performance
    So zünden Sie den Turbo
    Change Detection

    View Slide

  12. Basics
    // app.component.html
    Hi {{ title }}!

    Update

    // app.component.ts
    @Component({ /* … */ })
    export class AppComponent {
    title = 'Angular';
    update() {
    this.title = 'Foo';
    }
    }
    Angular Performance
    So zünden Sie den Turbo
    Change Detection
    Hi Foo!
    Hi Angular!
    Update

    View Slide

  13. Basics
    Change detection…
    - is the magical part of Angular that makes data binding “just work”
    - is a very handy feature that helps a lot, but it can also work against
    you
    - is strongly related to Angular application performance
    Angular Performance
    So zünden Sie den Turbo
    Change Detection

    View Slide

  14. Component Tree
    AppComponent
    NavComponent ContentComponent
    ListComponent
    Angular Performance
    So zünden Sie den Turbo
    Change Detection

    View Slide

  15. Change Detector Tree
    AppComponent
    NavComponent ContentComponent
    ListComponent
    Angular Performance
    So zünden Sie den Turbo
    Change Detection
    AppComponent CD
    NavComponent CD
    ContentComponent
    CD
    ListComponent CD
    CHANGE

    View Slide

  16. Change Detector
    detectChanges()
    Called when an event has occured and bindings should be checked
    Angular Performance
    So zünden Sie den Turbo
    Change Detection
    this.title = 'Foo'; Hi {{ title }}!

    View Slide

  17. Per default, each change in your application leads to…
    - A single CD cycle
    - From top to bottom (all components)
    - Unidirectional (no cycles allowed)
    Angular Performance
    So zünden Sie den Turbo
    Change Detection
    DEMO

    View Slide

  18. First findings
    Reduce duration of a change detection cycle
    - Reduce amount of bindings (e.g. grids: virtual scrolling via CDK)
    - Avoid binding to (computationally intensive) getters or functions
    Keep CD cycle < 16 ms!
    Angular Performance
    So zünden Sie den Turbo
    Change Detection

    View Slide

  19. Profiling
    // main.ts
    platformBrowserDynamic().bootstrapModule(AppModule).then(module =>
    enableDebugTools(module.injector.get(ApplicationRef).components[0]));
    Execute ng.profiler.timeChangeDetection() to measure the duration
    of a change detection run (500ms or 5 change detection cycles)
    Angular Performance
    So zünden Sie den Turbo
    Change Detection

    View Slide

  20. Command Line:
    ng new perf-demo --routing --style=css
    // src/main.ts
    platformBrowserDynamic().bootstrapModule(AppModule).then(module =>
    enableDebugTools(module.injector.get(ApplicationRef).components[0]));
    DevTools Console:
    ng.profiler.timeChangeDetection()
    Angular Performance
    So zünden Sie den Turbo
    Change Detection EX #1

    View Slide

  21. Runtime Performance
    Bundling
    Lazy
    Loading
    Preloading
    Strategies
    Server-Side
    Rendering
    Service
    Worker
    Angular Performance
    So zünden Sie den Turbo
    Agenda
    Change
    Detection
    Basics
    Zone.js &
    NgZone
    Change
    Detection
    Strategies
    Change
    Detector
    Async Pipe
    Load Time Performance

    View Slide

  22. How to detect a change?
    AppComponent
    NavComponent ContentComponent
    ListComponent
    Angular Performance
    So zünden Sie den Turbo
    Zone.js
    AppComponent CD
    NavComponent CD
    ContentComponent
    CD
    ListComponent CD
    CHANGE

    View Slide

  23. A look at Angular’s dependencies
    "dependencies": {
    "@angular/common": "~8.2.5",
    "rxjs": "~6.4.0",
    "tslib": "^1.10.0",
    "zone.js": "~0.9.1"
    },
    Zone.js
    So zünden Sie den Turbo
    Angular Performance

    View Slide

  24. Overview
    Provided by the Angular team
    Open-source
    https://github.com/angular/zone.js
    1. Provides an execution context for asynchronous JavaScript
    2. A meta-monkey patch
    Zone.js
    So zünden Sie den Turbo
    Angular Performance

    View Slide

  25. Execution Context
    function main() {
    // const start = performance.now();
    a();
    setTimeout(b, 0);
    c();
    // const stop = performance.now();
    // const ms = stop - start;
    }
    Zone.js
    So zünden Sie den Turbo
    Angular Performance

    View Slide

  26. Execution Context Oversimplified
    Zone.run(main);
    onZoneEnter();
    function main() {
    a();
    setTimeout(b, 0);
    c();
    }
    onZoneLeave();
    Zone.js
    const orig = window.setTimeout;
    window.setTimeout = (c, t) => {
    orig(() => {
    onZoneEnter();
    c();
    onZoneLeave();
    }, t);
    };
    So zünden Sie den Turbo
    Angular Performance

    View Slide

  27. Execution Context
    Debugging
    Pending asynchronous tasks are known
    Profiling
    Measuring performance (Google Web Tracing Framework)
    Mocking/Testing
    Hooks beforeTask, …
    Zone.js
    So zünden Sie den Turbo
    Angular Performance

    View Slide

  28. A Meta-Monkey Patch
    Zone.js
    setTimeout
    setInterval
    geolocation.getCurrentPosition
    XMLHttpRequest
    PromiseRejectionEvent
    requestAnimationFrame
    click
    focus
    mousemove
    addEventListener
    So zünden Sie den Turbo
    Angular Performance

    View Slide

  29. NgZone
    Angular Performance
    So zünden Sie den Turbo
    Zone.js
    current (global) zone
    NgZone
    Angular boot

    View Slide

  30. NgZone
    NgZone catches asynchronous
    operations from the Angular app
    When no tasks are remaining for
    the current VM turn, the NgZone
    will trigger a change detection
    cycle (tick)
    Angular Performance
    So zünden Sie den Turbo
    Zone.js
    NgZone
    setTimeout
    setInterval
    onclick
    Detect changes
    Detect changes
    Detect changes

    View Slide

  31. Change Detection Trigger
    https://github.com/angular/angular/blob/master/packages/core/src/application_ref.ts
    Angular Performance
    So zünden Sie den Turbo
    Zone.js
    NgZone.onMicrotaskEmpty ApplicationRef.tick()
    view1.detectChanges()
    view2.detectChanges()
    viewN.detectChanges()

    View Slide

  32. Common Pitfalls
    Long CD cycles in combination with high-frequency events
    - mousemove
    - scroll
    - requestAnimationFrame
    - setInterval with short intervals (clocks!)
    Angular Performance
    So zünden Sie den Turbo
    Zone.js
    DEMO

    View Slide

  33. NgZone
    Zone.js
    current (global) zone
    NgZone
    rAF
    Detect changes
    rAF
    Detect changes
    rAF
    Detect changes
    rAF
    Detect changes
    So zünden Sie den Turbo
    Angular Performance

    View Slide

  34. NgZone Opt-Out
    constructor (ngZone: NgZone) {
    ngZone.runOutsideAngular(() => {
    // runs outside Angular zone, for performance-critical code
    ngZone.run(() => {
    // runs inside Angular zone, for updating view afterwards
    });
    });
    }
    Zone.js
    So zünden Sie den Turbo
    Angular Performance
    ! View and model can
    get out of sync!
    DEMO

    View Slide

  35. NgZone Opt-Out
    // app.component.ts
    get title() { console.log('CD'); return 'title'; }
    ngOnInit() {
    requestAnimationFrame(() => this.ngOnInit());
    // this._ngZone.runOutsideAngular(() => request…);
    }
    constructor(private _ngZone: NgZone) {}
    Angular Performance
    So zünden Sie den Turbo
    Zone.js EX #2

    View Slide

  36. NgZone
    Zone.js
    current (global) zone
    NgZone
    rAF
    rAF
    rAF
    rAF
    So zünden Sie den Turbo
    Angular Performance

    View Slide

  37. Disable Patches (polyfills.ts)
    (window as any).__Zone_disable_requestAnimationFrame = true;
    // disable patch requestAnimationFrame
    (window as any).__Zone_disable_on_property = true;
    // disable patch onProperty such as onclick
    (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll',
    'mousemove'];
    // disable patch specified eventNames
    Angular Performance
    So zünden Sie den Turbo
    Zone.js
    ! View and model can
    get out of sync!

    View Slide

  38. Disable Zone (= disable async change detection!)
    platformBrowserDynamic().bootstrapModule(AppModule, {
    ngZone: 'noop'
    });
    constructor(applicationRef: ApplicationRef) {
    applicationRef.tick(); // trigger CD yourself
    }
    Angular Performance
    So zünden Sie den Turbo
    Zone.js
    ! View and model can
    get out of sync!

    View Slide

  39. Runtime Performance
    Bundling
    Lazy
    Loading
    Preloading
    Strategies
    Server-Side
    Rendering
    Service
    Worker
    Angular Performance
    So zünden Sie den Turbo
    Agenda
    Change
    Detection
    Basics
    Zone.js &
    NgZone
    Change
    Detection
    Strategies
    Change
    Detector
    Async Pipe
    Load Time Performance

    View Slide

  40. Overview
    Default
    Uses Zone.js for detecting
    changes and updates bindings
    OnPush
    Restricts change detection to
    changes of @Input parameters
    Angular Performance
    So zünden Sie den Turbo
    Change Detection Strategies
    AppComponent CD
    NavComponent CD
    ContentComponent
    CD
    ListComponent CD
    AppComponent CD
    NavComponent CD
    ContentComponent
    CD
    ListComponent CD
    OnPush

    View Slide

  41. OnPush


    @Component({
    selector: 'my-component',
    template: '{{ foo }}',
    changeDetection:
    ChangeDetectionStrategy.OnPush
    })
    export class MyComponent {
    @Input()
    public foo: string;
    }
    Change detection only reacts to
    changes of @Input parameters
    Angular compares the values
    passed to an @Input parameter
    (newValue === oldValue).
    If you are passing objects, make
    sure to pass in new instances!
    Angular Performance
    So zünden Sie den Turbo
    Change Detection Strategies
    ! View and model can
    get out of sync!
    DEMO

    View Slide

  42. Immutable.js to the rescue
    Immutable.js is a library that does not update data in place, but yields
    updated data instead
    https://facebook.github.io/immutable-js/
    const map1 = Immutable.Map({ a: 1, b: 2, c: 3 });
    const map2 = map1.set('b', 50);
    map1.get('b'); // 2
    map2.get('b'); // 50
    map2.toObject(); // { a: 1, b: 50, c: 3 }
    Change Detection Strategies
    So zünden Sie den Turbo
    Angular Performance

    View Slide

  43. // app.component.ts
    @Component({
    changeDetection: ChangeDetectionStrategy.OnPush
    })
    /* … */
    ngOnInit() {
    requestAnimationFrame(() => this.ngOnInit());
    // this._ngZone.runOutsideAngular(() => request…);
    }
    Angular Performance
    So zünden Sie den Turbo
    Change Detection Strategies EX #3

    View Slide

  44. Runtime Performance
    Bundling
    Lazy
    Loading
    Preloading
    Strategies
    Server-Side
    Rendering
    Service
    Worker
    Angular Performance
    So zünden Sie den Turbo
    Agenda
    Change
    Detection
    Basics
    Zone.js &
    NgZone
    Change
    Detection
    Strategies
    Change
    Detector
    Async Pipe
    Load Time Performance

    View Slide

  45. OnPush & Detecting Changes
    What to do if a component changes unrelated to an @Input parameter?
    constructor(private dataService: DataService) {}
    ngOnInit() {
    this.dataService.updates$
    .subscribe(newData => this.data = newData); // no update!
    }
    Angular Performance
    So zünden Sie den Turbo
    Change Detector

    View Slide

  46. ChangeDetectorRef
    constructor(cdRef: ChangeDetectorRef) {}
    A reference to the ChangeDetector of your component
    - detectChanges()
    - markForCheck()
    - detach()
    - checkNoChanges()
    - reattach()
    Angular Performance
    So zünden Sie den Turbo
    Change Detector

    View Slide

  47. markForCheck()
    Explicitly marks a component as dirty/changed (when using OnPush)
    Angular Performance
    So zünden Sie den Turbo
    Change Detector
    AppComponent CD
    NavComponent CD
    ContentComponent
    CD
    ListComponent CD
    DIRTY
    AppComponent CD
    NavComponent CD
    ContentComponent
    CD
    ListComponent CD
    OnPush

    View Slide

  48. markForCheck()
    constructor(private dataService: DataService,
    private cdRef: ChangeDetectorRef) {}
    ngOnInit() {
    this.dataService.updates$.subscribe(newData => {
    this.data = newData;
    this.cdRef.markForCheck();
    });
    }
    Angular Performance
    So zünden Sie den Turbo
    Change Detector

    View Slide

  49. Detaching Components
    changeDetector.detach(); changeDetector.reattach();
    Angular Performance
    So zünden Sie den Turbo
    Change Detector
    AppComponent CD
    NavComponent CD
    ContentComponent
    CD
    ListComponent CD
    AppComponent CD
    NavComponent CD
    ContentComponent
    CD
    ListComponent CD
    ! View and model can
    get out of sync!

    View Slide

  50. Local Change Detection
    constructor(cdRef: ChangeDetectorRef) {
    cdRef.detach(); // detaches this view from the CD tree
    // cdRef.detectChanges(); // detect this view & children
    // cdRef.reattach();
    }
    Angular Performance
    So zünden Sie den Turbo
    Change Detector

    View Slide

  51. Detaching components
    ng g c test
    Use in app.component.html
    // test.component.ts
    title = 'test';
    constructor(public cdRef: ChangeDetectorRef) { cdRef.detach(); }
    // test.component.html
    {{ title }}
    Angular Performance
    So zünden Sie den Turbo
    Change Detector EX #4

    View Slide

  52. Findings
    Reduce amount of change detection cycles
    - Disable Zone.js (not a good idea in most cases)
    - Opt-out of NgZone (for operations that should not affect bindings)
    - Disable Zone.js patches (in case you can’t opt-out, e.g. 3rd party libs)
    - ChangeDetectionStrategy.OnPush (good default, but be careful)
    - Local change detection via ChangeDetectorRef (for the few
    components that do not have to respond to changes from outside)
    Angular Performance
    So zünden Sie den Turbo
    Change Detector

    View Slide

  53. Runtime Performance
    Bundling
    Lazy
    Loading
    Preloading
    Strategies
    Server-Side
    Rendering
    Service
    Worker
    Angular Performance
    So zünden Sie den Turbo
    Agenda
    Change
    Detection
    Basics
    Zone.js &
    NgZone
    Change
    Detection
    Strategies
    Change
    Detector
    Async Pipe
    Load Time Performance

    View Slide

  54. Overview
    Takes observables, promises or synchronous values
    {{ data$ | async }}
    Waits for the observable to emit/promise to resolve and then displays
    the value
    Angular Performance
    So zünden Sie den Turbo
    Async Pipe

    View Slide

  55. Advantages
    For observables:
    - Async Pipe subscribes for you
    - Async Pipe takes care of unsubscribing from the observable
    - Async Pipe calls markForCheck for each update – perfect match for
    OnPush!
    https://github.com/angular/angular/blob/master/packages/common/src/pipes/async_pipe.ts
    Angular Performance
    So zünden Sie den Turbo
    Async Pipe

    View Slide

  56. Simplifying OnPush
    constructor(private dataService: DataService,
    private cdRef: ChangeDetectorRef) {}
    ngOnInit() {
    this.dataService.updates$.subscribe(newData => {
    this.data = newData;
    this.cdRef.markForCheck();
    });
    }
    Angular Performance
    So zünden Sie den Turbo
    Async Pipe

    View Slide

  57. Simplifying OnPush
    // component.ts
    data$: Observable;
    constructor(dataService: DataService) {
    this.data$ = this.dataService.updates$;
    }
    // component.html
    {{ data$ | async }}
    Angular Performance
    So zünden Sie den Turbo
    Async Pipe

    View Slide

  58. ng g c async
    Use in AppComponent
    Make AsyncComponent OnPush
    // async.component.ts
    title$ = of('foo').pipe(delay(1000));
    // async.component.html
    {{ title$ | async }}
    Angular Performance
    So zünden Sie den Turbo
    Async Pipe EX #5

    View Slide

  59. - Reduce initial load (size & computation)
    - Reduce perceived loading time
    - Prevent downloading the same resource again
    Angular Performance
    So zünden Sie den Turbo
    Load Time Performance

    View Slide

  60. Runtime Performance
    Bundling
    Lazy
    Loading
    Preloading
    Strategies
    Server-Side
    Rendering
    Service
    Worker
    Angular Performance
    So zünden Sie den Turbo
    Agenda
    Change
    Detection
    Basics
    Zone.js &
    NgZone
    Change
    Detection
    Strategies
    Change
    Detector
    Async Pipe
    Load Time Performance

    View Slide

  61. The Problem
    Angular’s development directory
    structure is hard to
    • deploy
    • serve
    • cache
    • …
    Lots of files, lots of requests
    Angular and its dependencies are large
    in size, apps use only a fragment
    Bundling
    So zünden Sie den Turbo
    Angular Performance

    View Slide

  62. The Problem
    Just-in-Time compilation (JiT)
    - Slow, client-side rendering
    - Compiler is 1.2 MB large in size
    - Template errors detected at runtime only
    - Potentially dangerous (injection attacks)
    Bundling
    So zünden Sie den Turbo
    Angular Performance

    View Slide

  63. The Problem
    Goal: Angular app
    - with all components pre-compiled
    - combined in a single (or few) file(s)
    - without redundant/unused code
    - uglified, compressed
    Bundling
    So zünden Sie den Turbo
    Angular Performance

    View Slide

  64. Angular Compilation
    Bundling
    Component
    @Component({ … })
    class UserComponent {
    user = { name: 'Chris' };
    }
    Template
    hello {{ user.name }}
    View Class
    var v = this.comp.user.name;
    So zünden Sie den Turbo
    Angular Performance

    View Slide

  65. {{ title }}
    Bundling
    So zünden Sie den Turbo
    Angular Performance

    View Slide

  66. JiT Compilation
    Bundling
    Component
    @Component({ … })
    class UserComponent {
    user = { name: 'Chris' };
    }
    Template
    hello {{ user.name }}
    Server Client
    Component
    @Component({ … })
    class UserComponent {
    user = { name: 'Chris' };
    }
    Template
    hello {{ user.name }}
    View Class
    var v = this.comp.user.name;
    So zünden Sie den Turbo
    Angular Performance

    View Slide

  67. AoT Compilation
    Bundling
    Component
    @Component({ … })
    class UserComponent {
    user = { name: 'Chris' };
    }
    Template
    hello {{ user.name }}
    Server Client
    Component
    @Component({ … })
    class UserComponent {
    user = { name: 'Chris' };
    }
    View Class
    var v = this.comp.user.name;
    Template
    hello {{ user.name }}
    View Class
    var v = this.comp.user.name;
    So zünden Sie den Turbo
    Angular Performance

    View Slide

  68. Tree Shaking
    “A Tree Shaker walks the dependency graph, top to bottom, and shakes
    out unused code like dead needles in a Christmas tree.”
    Bundling
    So zünden Sie den Turbo
    Angular Performance

    View Slide

  69. !function(){
    var obj = {
    foo: function () { // Hi there! },
    bar: function () { this.baz(); },
    baz: function () { // stub }
    };
    obj.foo();
    }();
    Tree Shaking Principle
    Bundling
    So zünden Sie den Turbo
    Angular Performance

    View Slide

  70. Differential Loading
    ES5: function add(a,b){return a+b}
    ES 2015: add=(a,b)=>a+b
    ES 2015 introduced shorter syntax (smaller footprint/saves bandwidth)
    Detect the platform and only deliver files required for this platform
    Larger ES5 bundles are only delivered to legacy browsers
    Angular Performance
    So zünden Sie den Turbo
    Bundling

    View Slide

  71. Differential Loading
    // index.html


    type="module">
    defer>

    Angular Performance
    So zünden Sie den Turbo
    Bundling

    View Slide

  72. Try some builds…
    ng build //no Tree Shaking, JiT
    ng build --aot //no Tree Shaking, AoT
    ng build --prod // Tree Shaking, AoT
    Don’t count Source Maps (*.js.map) and ES5 bundles.
    Bundling
    So zünden Sie den Turbo
    Angular Performance
    EX #6

    View Slide

  73. A Simple Demo App
    Dev build: 4.3 MB (ES 2015, without source maps)
    AoT build: 2.9 MB (ES 2015, without source maps)
    AoT+Tree Shaking: 313K (ES 2015, 97K gzipped)
    Bundling
    So zünden Sie den Turbo
    Angular Performance

    View Slide

  74. A Simple Demo App
    JiT build: ~19500 ms
    AoT build: ~13500 ms
    AoT+Tree Shaking: ~11500ms
    2018 MacBook Pro, served locally
    a few test runs, results may & will vary
    Bundling
    So zünden Sie den Turbo
    Angular Performance

    View Slide

  75. AoT Drawbacks
    Dynamic template compilation via ComponentFactoryResolver is
    discouraged in combination with AoT
    COMPILER_PROVIDERS are unavailable
    - https://github.com/angular/angular/issues/11780
    Bundling
    So zünden Sie den Turbo
    Angular Performance

    View Slide

  76. Runtime Performance
    Bundling
    Lazy
    Loading
    Preloading
    Strategies
    Server-Side
    Rendering
    Service
    Worker
    Angular Performance
    So zünden Sie den Turbo
    Agenda
    Change
    Detection
    Basics
    Zone.js &
    NgZone
    Change
    Detection
    Strategies
    Change
    Detector
    Async Pipe
    Load Time Performance

    View Slide

  77. Overview
    Angular router supports lazy loading components transparently
    Lazy loaded components are not delivered to/loaded by the client on
    boot, but on purpose
    Reduces load & perceived loading time
    Lazy Loading
    So zünden Sie den Turbo
    Angular Performance

    View Slide

  78. Architecture
    Lazy Loading is module-based
    Module is resolved via a dynamic import:
    import('./feature.module').then(m => m.FeatureModule)
    Routing is delegated to the feature module called
    Lazy Loading
    So zünden Sie den Turbo
    Angular Performance

    View Slide

  79. Overview
    const ROUTES: Routes = [{
    path: 'admin',
    loadChildren: () => import('app/admin/admin.module')
    .then(a => a.AdminModule)
    }];
    Lazy Loading
    So zünden Sie den Turbo
    Angular Performance

    View Slide

  80. ng g m lazy --routing
    ng g c lazy/lazy
    Add a route for LazyComponent to lazy/lazy-routing.module.ts
    const routes: Routes = [{ path: '', component: LazyComponent }];
    Configure app-routing.module.ts as follows:
    const routes: Routes = [{ path: 'lazy', loadChildren: () =>
    import('./lazy/lazy.module').then(l => l.LazyModule) }];
    Angular Performance
    So zünden Sie den Turbo
    Lazy Loading EX #7

    View Slide

  81. Runtime Performance
    Bundling
    Lazy
    Loading
    Preloading
    Strategies
    Server-Side
    Rendering
    Service
    Worker
    Angular Performance
    So zünden Sie den Turbo
    Agenda
    Change
    Detection
    Basics
    Zone.js &
    NgZone
    Change
    Detection
    Strategies
    Change
    Detector
    Async Pipe
    Load Time Performance

    View Slide

  82. Configuring Lazy Loading
    NoPreloading
    - does not preload any route by
    default
    - advantage: low size footprint
    - disadvantage: takes some
    time after clicking a link to the
    lazy-loaded module, not offline
    capable
    PreloadAllModules
    - automatically preloads all
    modules after the application
    has launched (still better
    loading time!)
    - advantage: lazy-loaded
    modules now load instant (also
    on the first click), offline
    capable
    - disadvantage: higher footprint
    Angular Performance
    So zünden Sie den Turbo
    Preloading Strategies

    View Slide

  83. Configuring Lazy Loading
    @NgModule({
    imports: [RouterModule.forRoot(routes, {
    preloadingStrategy: PreloadAllModules,
    })],
    exports: [RouterModule]
    })
    export class AppRoutingModule { }
    Angular Performance
    So zünden Sie den Turbo
    Preloading Strategies

    View Slide

  84. Custom Strategy
    It’s also possible to provide a custom strategy
    - when modules belong to each other
    - it’s expected that the user will access a certain module next (e.g.
    checkout after cart module)
    Developer can decide which modules should be preloaded on a
    navigation event
    Developer gets access to the modules which have not been preloaded
    yet
    Angular Performance
    So zünden Sie den Turbo
    Preloading Strategies

    View Slide

  85. Custom Strategy
    preload(route: Route, fn: () => Observable): Observable {
    // decide based on route (or other external information)
    // call fn to preload the module
    // otherwise, return of(null)
    }
    https://github.com/angular/angular/blob/8.2.10/packages/router/src/router_preloader.ts#L30-L58
    Angular Performance
    So zünden Sie den Turbo
    Preloading Strategies

    View Slide

  86. Custom Strategy
    ng g s myPreloadingStrategy (automatically adds @Injectable which is
    great for this demo, but typically you would not call it a service)
    // myPreloadingStrategy.service.ts
    export class MyPreloadingStrategyService implements PreloadingStrategy
    preload(route: Route, fn: () => Observable): Observable {
    console.log(route);
    return fn();
    }
    // app-routing.module.ts
    RouterModule.forRoot(routes, { preloadingStrategy:
    MyPreloadingStrategyService })
    Angular Performance
    So zünden Sie den Turbo
    Preloading Strategies EX #8

    View Slide

  87. Runtime Performance
    Bundling
    Lazy
    Loading
    Preloading
    Strategies
    Server-Side
    Rendering
    Service
    Worker
    Angular Performance
    So zünden Sie den Turbo
    Agenda
    Change
    Detection
    Basics
    Zone.js &
    NgZone
    Change
    Detection
    Strategies
    Change
    Detector
    Async Pipe
    Load Time Performance

    View Slide

  88. Angular Universal
    Renders Angular content on a server (i.e. not in a browser environment)
    Provided by the Angular team
    Open Source
    https://github.com/angular/universal
    Has CLI integration
    Server-Side Rendering
    So zünden Sie den Turbo
    Angular Performance

    View Slide

  89. Angular App
    Application Layer
    Rendering Layer
    Web Worker Server Browser NativeScript …
    Platform
    Server-Side Rendering
    So zünden Sie den Turbo
    Angular Performance

    View Slide

  90. Principle
    Pre-render the website using the same sources that are served
    Once Angular kicks in, the view is replaced with the client-rendered one
    Supports Node.js (Express), ASP.NET Core, Hapi.js & plain sockets
    JiT and AoT support (AoT strictly recommended for production)
    Server-Side Rendering
    So zünden Sie den Turbo
    Angular Performance

    View Slide

  91. Principle
    Server-Side Rendering
    Component
    @Component({ … })
    class UserComponent {
    user = { name: 'Chris' };
    }
    Template
    hello {{ user.name }}
    Server Client
    Component
    @Component({ … })
    class UserComponent {
    user = { name: 'Chris' };
    }
    View Class
    var v = this.comp.user.name;
    Template
    hello {{ user.name }}
    View Class
    var v = this.comp.user.name;
    index.html

    So zünden Sie den Turbo
    Angular Performance

    View Slide

  92. Purpose
    Search Engine Optimization
    Preview Links (Social Media)
    Graceful Degradation
    Reduce Perceived Loading Time/Quick First Contentful Paint (FCP)
    Improve Performance for Mobile/Low-Powered Devices
    Server-Side Rendering
    So zünden Sie den Turbo
    Angular Performance

    View Slide

  93. Server
    Rendering
    Asset
    Downloads
    Client Init Client Data Paint
    The Web App Gap
    Server-Side Rendering
    So zünden Sie den Turbo
    Angular Performance

    View Slide

  94. Preboot
    Filling the Web App Gap
    Records interactions of the user on the server-rendered part
    Replays the interaction once Angular kicks in on the client side
    Provided by the Angular team
    Open source
    https://github.com/angular/preboot
    Server-Side Rendering
    So zünden Sie den Turbo
    Angular Performance

    View Slide

  95. Server
    Rendering
    Asset
    Downloads
    Client Init Client Data Paint
    Preboot & The Web App Gap
    Angular Performance
    So zünden Sie den Turbo
    Server-Side Rendering
    Record Replay

    View Slide

  96. Preboot & Angular
    @NgModule({
    declarations: [AppComponent],
    imports: [
    BrowserModule.withServerTransition({ appId: 'foo' }),
    PrebootModule.withConfig({ appRoot: 'app-root' })
    ],
    bootstrap: [AppComponent]
    })
    export class AppModule { }
    Angular Performance
    So zünden Sie den Turbo
    Server-Side Rendering

    View Slide

  97. ng add @nguniversal/express-engine --clientProject perf-demo
    npm run build:ssr
    npm run serve:ssr
    http://localhost:4000
    Check Network tab in DevTools
    Angular Performance
    So zünden Sie den Turbo
    Server-Side Rendering EX #9

    View Slide

  98. Runtime Performance
    Bundling
    Lazy
    Loading
    Preloading
    Strategies
    Server-Side
    Rendering
    Service
    Worker
    Angular Performance
    So zünden Sie den Turbo
    Agenda
    Change
    Detection
    Basics
    Zone.js &
    NgZone
    Change
    Detection
    Strategies
    Change
    Detector
    Async Pipe
    Load Time Performance

    View Slide

  99. Idea: Never load the same resource twice
    Download resources once and store them in a local cache
    The next time the user wants to open the application, load the contents
    from there
    Makes your application sources offline-capable
    Significantly improves loading time
    Angular Performance
    So zünden Sie den Turbo
    Service Worker

    View Slide

  100. Key Technology
    Service Worker
    Service
    Worker
    Internet
    Website
    HTML/JS
    Cache
    fetch
    So zünden Sie den Turbo
    Angular Performance

    View Slide

  101. @angular/service-worker
    Service Worker implementation provided by the Angular team
    Features
    - Caching
    - Offline Availability
    - Push Notifications
    Service Worker is generated by the CLI (prod builds only)
    Service Worker
    So zünden Sie den Turbo
    Angular Performance

    View Slide

  102. Features
    ng add @angular/pwa
    ng build --prod
    cd dist/browser
    npx lite-server
    Service Worker
    So zünden Sie den Turbo
    Angular Performance
    EX #10

    View Slide

  103. Runtime Performance
    Don’t over-optimize
    Reduce duration of a change detection cycle
    - Reduce amount of bindings
    - Avoid binding to (computationally intensive) getters or functions
    Reduce amount of change detection cycles
    - Disable zone
    - NgZone
    - Zone.js patches
    - ChangeDetectorRef
    - ChangeDetectionStrategy
    Angular Performance
    So zünden Sie den Turbo
    Summary

    View Slide

  104. Thank you
    for your kind attention!
    Christian Liebel
    @christianliebel
    [email protected]

    View Slide