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. In unserem Thinktecture-Webinar zeigt Ihnen Chrsitian 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.

12c88a3a10478fa18d0363b3ba3d9df1?s=128

Christian Liebel
PRO

December 02, 2020
Tweet

Transcript

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

    Thinktecture Webinar
  2. Angular Performance: So zünden Sie den Turbo Thinktecture Webinar Agenda

    Change Detection & NgZone Change Detection Strategies Lazy Loading Service Workers
  3. Angular Performance: So zünden Sie den Turbo Thinktecture Webinar Agenda

    Change Detection & NgZone Change Detection Strategies Lazy Loading Service Workers
  4. Reduce required computations during runtime - calculations - painting -

    layouting - detecting changes Angular Performance: So zünden Sie den Turbo Thinktecture Webinar Runtime Performance
  5. Hi Angular! Basics // app.component.html <h1>Hi {{ title }}!</h1> //

    app.component.ts @Component({ /* … */ }) export class AppComponent { title = 'Angular'; } Angular Performance: So zünden Sie den Turbo Thinktecture Webinar Change Detection
  6. Basics // app.component.html <h1>Hi {{ title }}!</h1> <button (click)="update()"> Update

    </button> // app.component.ts @Component({ /* … */ }) export class AppComponent { title = 'Angular'; update() { this.title = 'Foo'; } } Angular Performance: So zünden Sie den Turbo Thinktecture Webinar Change Detection Hi Foo! Hi Angular! Update
  7. Component Tree AppComponent NavComponent ContentComponent ListComponent Angular Performance: So zünden

    Sie den Turbo Thinktecture Webinar Change Detection
  8. Change Detector Tree AppComponent NavComponent ContentComponent ListComponent Angular Performance: So

    zünden Sie den Turbo Thinktecture Webinar Change Detection AppComponent CD NavComponent CD ContentComponent CD ListComponent CD CHANGE
  9. 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 Thinktecture Webinar Change Detection DEMO
  10. 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 Thinktecture Webinar Change Detection
  11. 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 Thinktecture Webinar Change Detection DEMO
  12. How to detect a change? AppComponent NavComponent ContentComponent ListComponent Angular

    Performance: So zünden Sie den Turbo Thinktecture Webinar Zone.js AppComponent CD NavComponent CD ContentComponent CD ListComponent CD CHANGE
  13. A look at Angular’s dependencies "dependencies": { "@angular/common": "~11.0.1", "rxjs":

    "~6.6.0", "tslib": "^2.0.0", "zone.js": "~0.10.2" }, Zone.js Thinktecture Webinar Angular Performance: So zünden Sie den Turbo
  14. 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 Thinktecture Webinar Angular Performance: So zünden Sie den Turbo
  15. 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); }; Thinktecture Webinar Angular Performance: So zünden Sie den Turbo
  16. A Meta-Monkey Patch Zone.js setTimeout setInterval geolocation.getCurrentPosition XMLHttpRequest PromiseRejectionEvent requestAnimationFrame

    click focus mousemove addEventListener Thinktecture Webinar Angular Performance: So zünden Sie den Turbo
  17. NgZone Angular Performance: So zünden Sie den Turbo Thinktecture Webinar

    Zone.js current (global) zone NgZone Angular boot
  18. 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 Thinktecture Webinar Zone.js NgZone setTimeout setInterval onclick Detect changes Detect changes Detect changes
  19. Change Detection Trigger https://github.com/angular/angular/blob/master/packages/core/src/application_ref.ts Angular Performance: So zünden Sie den

    Turbo Thinktecture Webinar Zone.js NgZone.onMicrotaskEmpty ApplicationRef.tick() view1.detectChanges() view2.detectChanges() viewN.detectChanges()
  20. 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 Thinktecture Webinar Zone.js DEMO
  21. NgZone Zone.js current (global) zone NgZone rAF Detect changes rAF

    Detect changes rAF Detect changes rAF Detect changes Thinktecture Webinar Angular Performance: So zünden Sie den Turbo
  22. 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 Thinktecture Webinar Angular Performance: So zünden Sie den Turbo ! View and model can get out of sync! DEMO
  23. NgZone Zone.js current (global) zone NgZone rAF rAF rAF rAF

    Thinktecture Webinar Angular Performance: So zünden Sie den Turbo
  24. 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 Thinktecture Webinar Zone.js ! View and model can get out of sync!
  25. Event Coalescing <div (click)="doSomething()"> <button (click)="doSomethingElse()">Test</button> </div> // main.ts platformBrowserDynamic().bootstrapModule(AppModule,

    { ngZoneEventCoalescing: true }); Angular Performance: So zünden Sie den Turbo Thinktecture Webinar Zone.js ! App behavior may change! CHANGE CHANGE Coalescing
  26. 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 Thinktecture Webinar Zone.js ! View and model can get out of sync!
  27. Angular Performance: So zünden Sie den Turbo Thinktecture Webinar Agenda

    Change Detection & NgZone Change Detection Strategies Lazy Loading Service Workers
  28. 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 Thinktecture Webinar Change Detection Strategies AppComponent CD NavComponent CD ContentComponent CD ListComponent CD AppComponent CD NavComponent CD ContentComponent CD ListComponent CD OnPush
  29. OnPush <my-component [foo]="bar"> </my-component> @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 Thinktecture Webinar Change Detection Strategies ! View and model can get out of sync!
  30. // app.component.ts @Component({ changeDetection: ChangeDetectionStrategy.OnPush }) /* … */ ngOnInit()

    { requestAnimationFrame(() => this.ngOnInit()); // this._ngZone.runOutsideAngular(() => request…); } Angular Performance: So zünden Sie den Turbo Thinktecture Webinar Change Detection Strategies DEMO
  31. 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 Thinktecture Webinar Change Detector
  32. markForCheck() Explicitly marks a component as dirty/changed (when using OnPush)

    Angular Performance: So zünden Sie den Turbo Thinktecture Webinar Change Detector AppComponent CD NavComponent CD ContentComponent CD ListComponent CD DIRTY AppComponent CD NavComponent CD ContentComponent CD ListComponent CD OnPush
  33. 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 Thinktecture Webinar Change Detector
  34. 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 Thinktecture Webinar Async Pipe
  35. 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 Thinktecture Webinar Async Pipe
  36. 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 Thinktecture Webinar Async Pipe
  37. Simplifying OnPush // component.ts data$: Observable<string>; constructor(dataService: DataService) { this.data$

    = this.dataService.updates$; } // component.html {{ data$ | async }} Angular Performance: So zünden Sie den Turbo Thinktecture Webinar Async Pipe DEMO
  38. Detaching Components changeDetector.detach(); changeDetector.reattach(); Angular Performance: So zünden Sie den

    Turbo Thinktecture Webinar 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!
  39. 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: So zünden Sie den Turbo Thinktecture Webinar Change Detector
  40. - Reduce initial load (size & computation) - Reduce perceived

    loading time - Prevent downloading the same resource again Angular Performance: So zünden Sie den Turbo Thinktecture Webinar Load Time Performance
  41. Angular Performance: So zünden Sie den Turbo Thinktecture Webinar Agenda

    Change Detection & NgZone Change Detection Strategies Lazy Loading Service Workers
  42. 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 Thinktecture Webinar Angular Performance: So zünden Sie den Turbo
  43. 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 Thinktecture Webinar Angular Performance: So zünden Sie den Turbo
  44. Overview const ROUTES: Routes = [{ path: 'admin', loadChildren: ()

    => import('app/admin/admin.module') .then(a => a.AdminModule) }]; Lazy Loading Thinktecture Webinar Angular Performance: So zünden Sie den Turbo DEMO
  45. 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 Thinktecture Webinar Preloading Strategies
  46. Configuring Lazy Loading @NgModule({ imports: [RouterModule.forRoot(routes, { preloadingStrategy: PreloadAllModules, })],

    exports: [RouterModule] }) export class AppRoutingModule { } Angular Performance: So zünden Sie den Turbo Thinktecture Webinar Preloading Strategies
  47. 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 Thinktecture Webinar Preloading Strategies
  48. Unrelated To Routes Angular CLI generates bundles for all import()s

    and transparently downloads them when the function is invoked. function onClick() { import('./heavy-bundle').then(bundle => bundle.compute()); } Angular Performance: So zünden Sie den Turbo Thinktecture Webinar Lazy Loading
  49. Angular Performance: So zünden Sie den Turbo Thinktecture Webinar Agenda

    Change Detection & NgZone Change Detection Strategies Lazy Loading Service Workers
  50. 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 Thinktecture Webinar Service Worker
  51. Key Technology Service Worker Service Worker Internet Website HTML/JS Cache

    fetch Thinktecture Webinar Angular Performance: So zünden Sie den Turbo
  52. Commands ng add @angular/pwa ng build --prod cd dist/perf-demo npx

    lite-server Service Worker Thinktecture Webinar Angular Performance: So zünden Sie den Turbo DEMO
  53. 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: So zünden Sie den Turbo Thinktecture Webinar Load-Time Performance
  54. Thank you for your kind attention! Christian Liebel @christianliebel christian.liebel@thinktecture.com