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. Nach dem Laden einer Webanwendung muss sich diese schnell und flüssig anfühlen, damit der Anwender sein Ziel erreichen kann. Im Webinar zeigt Ihnen Christian Liebel mit Zone.js, Change Detection und Service Workern die wichtigsten Performance-Stellschrauben des Angular-Frameworks, damit Sie auch für Ihre Angular-App den Turbo zünden können. Melden Sie sich jetzt kostenlos an. Wir freuen uns auf Sie!

Christian Liebel
PRO

July 21, 2021
Tweet

More Decks by Christian Liebel

Other Decks in Programming

Transcript

  1. Angular Performance
    Your App at the Speed of Light
    Christian Liebel
    @christianliebel
    Consultant
    P

    View Slide

  2. Hello, it’s me.
    Angular Performance
    Your App at the Speed of Light
    Christian Liebel
    Twitter:
    @christianliebel
    Email:
    christian.liebel
    @thinktecture.com
    Angular & PWA
    Slides:
    thinktecture.com
    /christian-liebel

    View Slide

  3. Angular Performance
    Your App at the Speed of Light
    Agenda
    Change
    Detection &
    NgZone
    Change
    Detection
    Strategies
    Service
    Workers

    View Slide

  4. Angular Performance
    Your App at the Speed of Light
    Agenda
    Change
    Detection &
    NgZone
    Change
    Detection
    Strategies
    Service
    Workers

    View Slide

  5. Reduce required computations during runtime
    - calculations
    - painting
    - layouting
    - detecting changes
    Angular Performance
    Your App at the Speed of Light
    Runtime Performance

    View Slide

  6. Hi Angular!
    Basics
    // app.component.html
    Hi {{ title }}!
    // app.component.ts
    @Component({ /* … */ })
    export class AppComponent {
    title = 'Angular';
    }
    Angular Performance
    Your App at the Speed of Light
    Change Detection

    View Slide

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

    Update

    // app.component.ts
    @Component({ /* … */ })
    export class AppComponent {
    title = 'Angular';
    update() {
    this.title = 'Foo';
    }
    }
    Angular Performance
    Your App at the Speed of Light
    Change Detection
    Hi Foo!
    Hi Angular!
    Update

    View Slide

  8. Component Tree
    AppComponent
    NavComponent ContentComponent
    ListComponent
    Angular Performance
    Your App at the Speed of Light
    Change Detection

    View Slide

  9. Change Detector Tree
    AppComponent
    NavComponent ContentComponent
    ListComponent
    Angular Performance
    Your App at the Speed of Light
    Change Detection
    AppComponent CD
    NavComponent CD
    ContentComponent
    CD
    ListComponent CD
    CHANGE

    View Slide

  10. 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
    Your App at the Speed of Light
    Change Detection
    DEMO

    View Slide

  11. 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
    Your App at the Speed of Light
    Change Detection

    View Slide

  12. 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
    Your App at the Speed of Light
    Change Detection
    DEMO

    View Slide

  13. Profiling
    Angular Performance
    Your App at the Speed of Light
    Change Detection
    DEMO

    View Slide

  14. How to detect a change?
    AppComponent
    NavComponent ContentComponent
    ListComponent
    Angular Performance
    Your App at the Speed of Light
    Zone.js
    AppComponent CD
    NavComponent CD
    ContentComponent
    CD
    ListComponent CD
    CHANGE

    View Slide

  15. A look at Angular’s dependencies
    "dependencies": {
    "@angular/common": "~11.2.0",
    "rxjs": "~6.6.0",
    "tslib": "^2.0.0",
    "zone.js": "~0.10.2"
    },
    Zone.js
    Your App at the Speed of Light
    Angular Performance

    View Slide

  16. 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
    Your App at the Speed of Light
    Angular Performance

    View Slide

  17. 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);
    };
    Your App at the Speed of Light
    Angular Performance

    View Slide

  18. A Meta-Monkey Patch
    Zone.js
    setTimeout
    setInterval
    geolocation.getCurrentPosition
    XMLHttpRequest
    PromiseRejectionEvent
    requestAnimationFrame
    click
    focus
    mousemove
    addEventListener
    Your App at the Speed of Light
    Angular Performance

    View Slide

  19. NgZone
    Angular Performance
    Your App at the Speed of Light
    Zone.js
    current (global) zone
    NgZone
    Angular boot

    View Slide

  20. 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
    Your App at the Speed of Light
    Zone.js
    NgZone
    setTimeout
    setInterval
    onclick
    Detect changes
    Detect changes
    Detect changes

    View Slide

  21. Change Detection Trigger
    https://github.com/angular/angular/blob/master/packages/core/src/application_ref.ts
    Angular Performance
    Your App at the Speed of Light
    Zone.js
    NgZone.onMicrotaskEmpty ApplicationRef.tick()
    view1.detectChanges()
    view2.detectChanges()
    viewN.detectChanges()

    View Slide

  22. Common Pitfalls
    Long CD cycles in combination with high-frequency events
    - mousemove
    - scroll
    - requestAnimationFrame
    - setInterval with short intervals (clocks!)
    Angular Performance
    Your App at the Speed of Light
    Zone.js
    DEMO

    View Slide

  23. NgZone
    Zone.js
    current (global) zone
    NgZone
    rAF
    Detect changes
    rAF
    Detect changes
    rAF
    Detect changes
    rAF
    Detect changes
    Your App at the Speed of Light
    Angular Performance

    View Slide

  24. 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
    Your App at the Speed of Light
    Angular Performance
    ! View and model can
    get out of sync!
    DEMO

    View Slide

  25. NgZone
    Zone.js
    current (global) zone
    NgZone
    rAF
    rAF
    rAF
    rAF
    Your App at the Speed of Light
    Angular Performance

    View Slide

  26. 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
    Your App at the Speed of Light
    Zone.js
    ! View and model can
    get out of sync!

    View Slide

  27. Event Coalescing

    Test

    // main.ts
    platformBrowserDynamic().bootstrapModule(AppModule, {
    ngZoneEventCoalescing: true
    });
    Angular Performance
    Your App at the Speed of Light
    Zone.js
    ! App behavior
    may change!
    CHANGE
    CHANGE
    Coalescing

    View Slide

  28. Disable Zone (= disable async change detection!)
    platformBrowserDynamic().bootstrapModule(AppModule, {
    ngZone: 'noop'
    });
    constructor(applicationRef: ApplicationRef) {
    applicationRef.tick(); // trigger CD yourself
    }
    Angular Performance
    Your App at the Speed of Light
    Zone.js
    ! View and model can
    get out of sync!

    View Slide

  29. Angular Performance
    Your App at the Speed of Light
    Agenda
    Change
    Detection &
    NgZone
    Change
    Detection
    Strategies
    Service
    Workers

    View Slide

  30. Overview
    Default
    Uses Zone.js for detecting
    changes and updates bindings
    OnPush
    Restricts change detection to
    changes of @Input parameters
    Angular Performance
    Your App at the Speed of Light
    Change Detection Strategies
    AppComponent CD
    NavComponent CD
    ContentComponent
    CD
    ListComponent CD
    AppComponent CD
    NavComponent CD
    ContentComponent
    CD
    ListComponent CD
    OnPush
    P

    View Slide

  31. OnPush


    @Component({
    selector: 'my-component',
    template: '{{ foo }}',
    changeDetection:
    ChangeDetectionStrategy.OnPush
    })
    export class MyComponent {
    @Input()
    public foo: string;
    }
    Change detection only reacts to
    events within the component and
    changes of @Input() parameters
    Angular compares the values
    passed to an @Input() via
    Object.is(oldValue, newValue)
    If you are passing objects, make
    sure to pass in new instances!
    Angular Performance
    Your App at the Speed of Light
    Change Detection Strategies
    ! View and model can
    get out of sync!

    View Slide

  32. // app.component.ts
    @Component({
    changeDetection: ChangeDetectionStrategy.OnPush
    })
    /* … */
    ngOnInit() {
    requestAnimationFrame(() => this.ngOnInit());
    // this._ngZone.runOutsideAngular(() => request…);
    }
    Angular Performance
    Your App at the Speed of Light
    Change Detection Strategies
    DEMO

    View Slide

  33. 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
    Your App at the Speed of Light
    Change Detector

    View Slide

  34. markForCheck()
    Explicitly marks a component as dirty/changed (when using OnPush)
    Angular Performance
    Your App at the Speed of Light
    Change Detector
    AppComponent CD
    NavComponent CD
    ContentComponent
    CD
    ListComponent CD
    DIRTY
    AppComponent CD
    NavComponent CD
    ContentComponent
    CD
    ListComponent CD
    OnPush

    View Slide

  35. markForCheck()
    constructor(private dataService: DataService,
    private cdRef: ChangeDetectorRef) {}
    ngOnInit() {
    this.dataService.updates$.subscribe(newData => {
    this.data = newData;
    this.cdRef.markForCheck();
    });
    }
    Angular Performance
    Your App at the Speed of Light
    Change Detector

    View Slide

  36. 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
    Your App at the Speed of Light
    Async Pipe

    View Slide

  37. 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
    Your App at the Speed of Light
    Async Pipe

    View Slide

  38. Simplifying OnPush
    constructor(private dataService: DataService,
    private cdRef: ChangeDetectorRef) {}
    ngOnInit() {
    this.dataService.updates$.subscribe(newData => {
    this.data = newData;
    this.cdRef.markForCheck();
    });
    }
    Angular Performance
    Your App at the Speed of Light
    Async Pipe

    View Slide

  39. Simplifying OnPush
    // component.ts
    data$: Observable;
    constructor(dataService: DataService) {
    this.data$ = this.dataService.updates$;
    }
    // component.html
    {{ data$ | async }}
    Angular Performance
    Your App at the Speed of Light
    Async Pipe
    DEMO

    View Slide

  40. Detaching Components
    changeDetector.detach(); changeDetector.reattach();
    Angular Performance
    Your App at the Speed of Light
    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

  41. Findings
    Reduce amount of change detection cycles
    - Disable Zone.js (not a good idea in most cases)
    - Coalesce CD cycles (might change behavior of existing apps)
    - 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
    Your App at the Speed of Light
    Change Detector

    View Slide

  42. - Reduce initial load (size & computation)
    - Reduce perceived loading time
    - Prevent downloading the same resource again
    Angular Performance
    Your App at the Speed of Light
    Load Time Performance

    View Slide

  43. Angular Performance
    Your App at the Speed of Light

    View Slide

  44. Angular Performance
    Your App at the Speed of Light
    Agenda
    Change
    Detection &
    NgZone
    Change
    Detection
    Strategies
    Service
    Workers

    View Slide

  45. 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
    Your App at the Speed of Light
    Service Worker

    View Slide

  46. Key Technology
    Service Worker
    Service
    Worker
    Internet
    Website
    HTML/JS
    Cache
    fetch
    Your App at the Speed of Light
    Angular Performance

    View Slide

  47. Commands
    ng add @angular/pwa
    ng build -c production
    cd dist/perf-demo
    npx lite-server
    Service Worker
    Your App at the Speed of Light
    Angular Performance
    DEMO

    View Slide

  48. Findings
    – Prevent downloading the same resource twice by using the Service Worker
    – Postpone loading less frequently used parts of your application by leveraging lazy
    loading
    Angular Performance
    Your App at the Speed of Light
    Load-Time Performance

    View Slide

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

    View Slide