Slide 1

Slide 1 text

AngularArchitects.io | @ManfredSteyer Alexander Thalhammer | @LX_T Angular Initial Load Performance Optimization https://github.com/L-X-T/ng-days-2024

Slide 2

Slide 2 text

Agenda Performance Tools Initial Load Performance Runtime Perf & Change Det. Q & A or Lightning

Slide 3

Slide 3 text

Agenda Initial Load Performance Lazy Loading Images & Build Opt. Lazy Load. JS & Preloading Deferrable Views SSR & SSG

Slide 4

Slide 4 text

Timetable Performance Optimization − now – 9:30 Performance Tools − 9:30 – 10:30 Initial Load Lazy stuff − 10:30 – 11:00 break − 11:00 – 11:45 Initial Load modern shoot − 11:45 – 12:15 Runtime Perf − 12:15 – 12:30 Q&A else

Slide 5

Slide 5 text

About me …

Slide 6

Slide 6 text

My office ☺

Slide 7

Slide 7 text

Hi, it's me → @LX_T • Alex Thalhammer from Graz, Austria (since 1983) • Angular Software Tree GmbH (since 2019) • WebDev for 22+ years (I‘ve come a long way baby) • WordPress Dev (Web, PHP & jQuery, 2011 - 2017) • Angular Dev (Web, TS, Rx since 2017 - NG 4.0.0 ☺) • Angular Evangelist, Coach & Consultant (since 2020) • Member of Angular Architects https://www.angulararchitects.io/ https://alex.thalhammer.name/

Slide 8

Slide 8 text

Pollings • OS • Mobile OS? • Browser • IntelliJ/WebStorm vs VS Code vs NeoVIM • Which framework is the fastest?

Slide 9

Slide 9 text

Angular Performance Optimization WTF?

Slide 10

Slide 10 text

Angular Performance Optimization We distinguish between • Initial load performance (classical web performance) • Runtime performance (during usage, e.g. scrolling frame rate)

Slide 11

Slide 11 text

Starter Kit • Clone from https://github.com/L-X-T/ng-days-2024

Slide 12

Slide 12 text

Ready for Takeoff! • What are your questions?

Slide 13

Slide 13 text

Img src: https://bit.ly/ng-tools-img

Slide 14

Slide 14 text

Agenda Audit Tools − PageSpeed & Lighthouse − Source Map Explorer − Import Graph Visualizer − Chrome DevTools − Angular DevTools Profiler

Slide 15

Slide 15 text

Agenda Audit Tools − PageSpeed & Lighthouse − Source Map Explorer − Import Graph Visualizer − Chrome DevTools − Angular DevTools Profiler Initial Load Bundle Size Runtime

Slide 16

Slide 16 text

Web Audit Tools

Slide 17

Slide 17 text

PageSpeed Insights vs Chrome Lighthouse − PageSpeed Insights − Real user data & Lighthouse lab results − Go to pagespeed.web.dev & enter URL − Test is being run on Google servers − Performance, new since 2023 − Accessibility − Best Practices − SEO − Chrome Lighthouse extension − Open URL in your browser (run in incognito mode, close other Apps) − Open Lighthouse tab → Analyze − Performance, other tests − Accessibility − Best Practices − SEO − PWA

Slide 18

Slide 18 text

PageSpeed Score – Real Users (Origin)

Slide 19

Slide 19 text

PageSpeed Score – Lab Data (URL)

Slide 20

Slide 20 text

Demo PageSpeed Insights

Slide 21

Slide 21 text

What's being measured? − Time to First Byte (TTFB) − First Contentful Paint (FCP) − Speed Index (originally by WebPageTest) − Largest Contentful Paint (LCP) − Time to Interactive (TTI) − Total Blocking Time (TBT) → TTI - FCP − Cumulative Layout Shift (CLS) − First Input Delay (FID) − Interaction to Next Paint (INP) → new! PageSpeed Insights & Chrome Lighthouse

Slide 22

Slide 22 text

Demo Chrome Lighthouse

Slide 23

Slide 23 text

Build Analysis Tools

Slide 24

Slide 24 text

− Needs generated source maps to work − Either set in build options (angular.json) − Or just use the build flag "--source-map" − Analyzes a single js file or whole bundle / build − main bundle − or lazy loading bundles − Determines where each byte in your code comes from − Shows a black/white treemap visualization of build size Source Map Explorer

Slide 25

Slide 25 text

Source Map Explorer

Slide 26

Slide 26 text

Demo Source Map Explorer https://www.npmjs.com/package/source-map-explorer

Slide 27

Slide 27 text

− A development tool for filtering and visualizing import paths within a JavaScript/TypeScript application − Allows filtering import paths by source & target modules − Allows zooming in to a limited subsection of your app, which will likely be easier to analyze than the entire app Import Graph Visualizer

Slide 28

Slide 28 text

Import Graph Visualizer

Slide 29

Slide 29 text

− npx import-graph-visualizer --entry-points path/to/entry/module --ts-config path/to/tsconfig − e.g. npx import-graph-visualizer --entry-points src/main.ts - -ts-config tsconfig.app.json Import Graph Visualizer – How To

Slide 30

Slide 30 text

Demo Import Graph Visualizer https://github.com/rx-angular/import-graph-visualizer

Slide 31

Slide 31 text

− The Chrome DevTools are not only used for − Styling (Elements) − Debugging (Console) − But also for Performance − Network − Performance − Memory − memory heap comparison − Performance Insights (Beta!) Google Chrome DevTools

Slide 32

Slide 32 text

Google Chrome DevTools

Slide 33

Slide 33 text

Demo Chrome DevTools

Slide 34

Slide 34 text

− Angular DevTools extension can be added to Chrome − https://chromewebstore.google.com/detail/angular- devtools/ienfalfjdbdpebioblfackkekamfmbnh − Features a Component Tree to inspect the components − Profiler − And the newly added Injector Tree (Dependency Injection) − Profiler shows individual change detection (CD) cycles − What triggered CD − How much time it took executing CD Angular DevTools Profiler

Slide 35

Slide 35 text

Angular DevTools Profiler

Slide 36

Slide 36 text

Demo Angular DevTools Profiler

Slide 37

Slide 37 text

Recap Audit Tools − PageSpeed & Lighthouse − WebPageTest.org − Source Map Explorer − Webpack Bundle Analyzer − Vite Bundle Visualizer − Import Graph Visualizer − Chrome DevTools − Angular DevTools Profiler Initial Load Bundle Size Runtime

Slide 38

Slide 38 text

− Google Web Dev − https://pagespeed.web.dev − https://web.dev/metrics/ − Improving Load Performance - Chrome DevTools 101 − https://www.youtube.com/watch?v=5fLW5Q5ODiE − How to analyze your JavaScript bundles − https://www.youtube.com/watch?v=MxBCPc7bQvM References

Slide 39

Slide 39 text

Questions?

Slide 40

Slide 40 text

AngularArchitects.io | @ManfredSteyer Alexander Thalhammer | @LX_T Initial Load Performance Assets & Build

Slide 41

Slide 41 text

Outline - Initial Load Performance • Assets & Build • Lazy Loading • Deferrable Views • SSR & SSG

Slide 42

Slide 42 text

Outline - Assets & Build • Use web performance best practices • Use NgOptimizedImage (since NG 14.2.0) • Use build optimization correctly • Avoid large 3rd party libs / CSS frameworks

Slide 43

Slide 43 text

Web Performance Best Practices

Slide 44

Slide 44 text

Use web performance best practices Solutions: • Images not optimized → Use .webp, .avif or .svg • Images not properly sized → Use srcsets • Too large assets, too many assets → Clean up & lazy load assets • Unused JS code or CSS styles → Clean up & lazy loading / deferring • Slow server infrastructure → HTTP/3, CDN • Caching not configured correctly → Configure it • Compression not configured correctly → Brotli or Gzip • …

Slide 45

Slide 45 text

Use NgOptimizedImage (since NG 14.2.0) • Problem: Lighthouse or PageSpeed report image errors / warnings • Identify: Lighthouse & PageSpeed Insights / WebPageTest or DevTools • Solution: Use NgOptimizedImage's ngSrc instead of src attribute • Takes care of intelligent lazy loading • Prioritization of critical images ("above-the-fold") • Also creates srcset & sizes attributes (for responsive sizes, since NG 15.0.0) • Also supports high-resolution devices ("Retina images")

Slide 46

Slide 46 text

Demo NgOptimizedImage

Slide 47

Slide 47 text

Build optimization

Slide 48

Slide 48 text

Advantages of Angular Ivy (since V9) • Angular ViewEngine itself was not tree-shakable • Default since NG 10, for libs default since NG 12 • AOT per default → You don't need to include the compiler! • Ivy also does a lot of under the hood optimization • Tools can easier analyse the code • Remove unneeded parts of frameworks • Called Tree Shaking • Also 3rd party and • Even our own libs

Slide 49

Slide 49 text

Tree Shaking

Slide 50

Slide 50 text

Tree Shaking

Slide 51

Slide 51 text

Use Build Optimization Solution: • Use production build • ng b(uild) (--c production) • Set up angular.json (or project.json) correctly

Slide 52

Slide 52 text

Demo Build Configuration

Slide 53

Slide 53 text

Avoid large 3rd party libs / CSS frameworks • Problem: Importing large 3rd party libraries that are not treeshakable • moment • lodash • charts • … • Identify: Source Map Analyzer or Webpack Bundle Analyzer • Solution: Remove or replace that lib / framework • moment → luxon, day.js or date-fns • lodash → lodash-es • …

Slide 54

Slide 54 text

Demo Large Libs

Slide 55

Slide 55 text

Recap • Use web performance best practices • Use NgOptimizedImage (since NG 14.2.0) • Use build optimization correctly • Avoid large 3rd party libs / CSS frameworks

Slide 56

Slide 56 text

References • Optimize the bundle size of an Angular application • https://www.youtube.com/watch?v=19T3O7XWJkA • Angular Docs • NgOptimizedImage • NG Build

Slide 57

Slide 57 text

Questions?

Slide 58

Slide 58 text

AngularArchitects.io | @ManfredSteyer Alexander Thalhammer | @LX_T Initial Load Performance Lazy Loading

Slide 59

Slide 59 text

Outline - Initial Load Performance • Assets & Build • Lazy Loading • Deferrable Views • SSR & SSG

Slide 60

Slide 60 text

Outline - Lazy Loading & Deferrable Views • Lazy Loading via modules / features • 2 most common pitfalls and their solutions • Lazy Loading via standalone components • Preloading • PreloadAllModules • Other strategies • Lazy Loading without the router (a bit complicated) • Lazy Loading below the fold (very, very complicated) • Deferring (brand new in NG 17, very lean, replaces last 2)

Slide 61

Slide 61 text

Lazy Loading (Angular 5)

Slide 62

Slide 62 text

Angular Module | Feature Structure AppModule … … … Root Module Feature Modules Shared Module SharedModule

Slide 63

Slide 63 text

Angular Lazy Loading – Theory AppModule … Root Module Feature Modules Shared Module SharedModule

Slide 64

Slide 64 text

Angular Lazy Loading – Common Pitfall AppModule … Root Module Feature Modules Huge Shared Module SharedModule

Slide 65

Slide 65 text

Angular Lazy Loading - Solution AppModule … Root Module Feature Modules Small Shared Modules SharedModule SharedModule

Slide 66

Slide 66 text

Angular Lazy Loading – Another Pitfall AppModule Home / Login Root Module Feature Modules Small Shared Modules SharedModule SharedModule

Slide 67

Slide 67 text

Angular Lazy Loading – Solution AppModule Feature Root Module Feature Modules Small Shared Modules SharedModule SharedModule Home / Login

Slide 68

Slide 68 text

App Routes with Lazy Loading Page ▪ 68 export const appRoutes: Routes = [ { path: 'home', component: HomeComponent }, { path: 'flights_module', loadChildren: () => import('./flights/flights.module') .then((m) => m.FlightsModule) }, { path: 'flights_standalone', loadChildren: () => import('./flights/flights.routes') } ];

Slide 69

Slide 69 text

Routes for "lazy" Feature export const flightsRoutes: Routes = [ { path: 'flight-search', component: FlightSearchComponent, […] }, […] } export default flightsRoutes; // for standalone flights/flight-search Triggers Lazy Loading w/ loadChildren

Slide 70

Slide 70 text

Demo Lazy Loading Module

Slide 71

Slide 71 text

Lazy Loading with standalone components export const appRoutes: Routes = [ { path: 'home', component: HomeComponent }, { path: 'charts', loadComponent: () => import('./charts/charts.component') .then((c) => c.ChartsComponent) } ];

Slide 72

Slide 72 text

Demo Lazy Loading Standalone

Slide 73

Slide 73 text

What about services? … @Injectable({ providedIn: 'root' }) … • When used by 1 lazy loaded module/comp exlusively it will be put into that chunk • When used by 2 or more lazy loaded modules/comps it will be put into a common chunk • When used by an eagerly loaded part it will be put into main bundle

Slide 74

Slide 74 text

Use Lazy Loading a lot Problem: • Loading to much source (libs / components) at startup • Resulting in a big main bundle (and vendor if used) Identify: • Not using lazy loading throughout the App (source code) • Webpack Bundle Analyzer or • Source Map Explorer

Slide 75

Slide 75 text

Lazy Loading • Lazy Loading means: Load it later, after startup • Better initial load performance • But: Delay during execution for loading on demand

Slide 76

Slide 76 text

Preloading

Slide 77

Slide 77 text

Preloading • Once the initial load (the important one) is complete load the lazy loaded modules (before they are even used) • When module is needed it is available immediately

Slide 78

Slide 78 text

Activate Preloading (in AppModule) … imports: [ […] RouterModule.forRoot( appRoutes, { preloadingStrategy: PreloadAllModules } ); ] …

Slide 79

Slide 79 text

Activate Preloading (in app.config.ts, NG15) … providers: [ […] provideRouter( appRoutes, withPreloading(PreloadAllModules), ), ] …

Slide 80

Slide 80 text

Demo Preloading

Slide 81

Slide 81 text

Intelligent Preloading with ngx-quicklink … imports: [ […] QuicklinkModule, RouterModule.forRoot( appRoutes, { preloadingStrategy: QuicklinkStrategy } ); ] … https://web.dev/route-preloading-in-angular/ https://www.npmjs.com/package/ngx-quicklink

Slide 82

Slide 82 text

Demo Ngx Quicklink

Slide 83

Slide 83 text

Or CustomPreloadingStrategy Page ▪ 84 … imports: [ […] RouterModule.forRoot( appRoutes, { preloadingStrategy: CustomPreloadingStrategy } ); ] …

Slide 84

Slide 84 text

Use Lazy Loading a lot - but carefully ;-) Solution: • Implement lazy loading whereever you can • Use lazy loading with the router • Modules • Components (new since NG15!) • Maybe use a CustomPreloadingStrategy if App is very big • Use dynamic components • Use Import Graph Visualizer to detect why things land in main bundle • But don't lazyload the initial feature, because it will be delayed ;-) • And don't destroy lazy loading by (eagerly) loading a huge shared module

Slide 85

Slide 85 text

Lazy Loading without the router … @ViewChild('cnt', { read: ViewContainerRef }) vCR!: ViewContainerRef; … async ngOnInit() { const esm = await import('./lazy/lazy.component'); const lazyComponentRef = this.vCR.createComponent(esm.LazyComponent); } … … …

Slide 86

Slide 86 text

Demo Dynamic Lazy Loading

Slide 87

Slide 87 text

Recap • Lazy Loading via modules / features • Avoid 2 most common pitfalls and their solutions • Lazy Loading via standalone components • Preloading • PreloadAllModules • QuicklinkStrategy (ngx-quicklink)

Slide 88

Slide 88 text

References • Angular Docs • Lazy-loading feature modules

Slide 89

Slide 89 text

Questions?

Slide 90

Slide 90 text

AngularArchitects.io | @ManfredSteyer Alexander Thalhammer | @LX_T Initial Load Performance Deferrable Views

Slide 91

Slide 91 text

Outline - Initial Load Performance • Assets & Build • Lazy Loading • Deferrable Views • SSR & SSG

Slide 92

Slide 92 text

Deferrable Views (Angular 17) • Problem: Lazy Loading without the router and especially Lazy Loading below the fold is rather complicated and inconvenient • NG17 has the solution in the new template syntax control flow: • It's called: Deferrable Views

Slide 93

Slide 93 text

Deferrable Views - syntax … @defer (on viewport) { } @placeholder {

Component is loading on viewport.

} …

Slide 94

Slide 94 text

Deferrable Views - on • on immediate (default) • on viewport • on hover • on interaction • on timer(4200ms)

Slide 95

Slide 95 text

Deferrable Views - when • specifies an imperative condition as an expression that returns a bool • best used: boolean flag • if the condition returns to false, the swap is not reverted • it is a one-time operation … @defer (when condition) { } …

Slide 96

Slide 96 text

Deferrable Views - prefetch • allows to specify conditions when prefetching of the dependencies should be triggered … @defer (on viewport; prefetch on idle) { } …

Slide 97

Slide 97 text

Deferrable Views - extras • @placeholder • @loading • @error … @defer (on viewport; prefetch on idle) { } @placeholder (minimum 500ms) { lazy component placeholder } @loading (after 500ms; minimum 1s) { lazy is loading spinner } @error {

Why do I exist?

} …

Slide 98

Slide 98 text

Demo Deferrable Views

Slide 99

Slide 99 text

References • Angular Docs • Lazy-loading feature modules • Angular Architects Blog • Deferrable Views (Blog post)

Slide 100

Slide 100 text

Questions?

Slide 101

Slide 101 text

AngularArchitects.io | @ManfredSteyer Alexander Thalhammer | @LX_T Initial Load Performance SSR & SSG

Slide 102

Slide 102 text

Outline - Initial Load Performance • Assets & Build • Lazy Loading • Deferrable Views • SSR & SSG

Slide 103

Slide 103 text

Outline - SSR & SSG • Server-Side Rendering • Hydration • Prerendering • Alternative: Use a URL cache

Slide 104

Slide 104 text

Server-side rendering

Slide 105

Slide 105 text

Server-side rendering (SSR) • Problem: After download rendering on the client takes too much time • Search Engines may not be abled to index the App correctly • Identify: After .js files have been loaded js main thread takes too long • Search Engines don’t index correctly • Solution: Use Angular Universal • Page is rendered on the server and then served to the client • But only useful for public pages (no user login)

Slide 106

Slide 106 text

Server-Side Rendering (Angular 16) • New feature called “non destructive hydration” export const appConfig: ApplicationConfig = { providers: [ provideClientHydration(), [...] ], };

Slide 107

Slide 107 text

Demo Server Side Rendering

Slide 108

Slide 108 text

Prerender important routes (SSG) • Problem: Server response takes to long cos page has to be rendered • Identify : Long server response time when using Universal SSR • Solution: Prerender the important pages on the server • Built-in Angular Universal since V11 • Then serve them rendered to the user

Slide 109

Slide 109 text

Demo Static Site Generation

Slide 110

Slide 110 text

Event Replay (Angular 18) • Problem: Clicking and interacting with app before hydration • Identify : Long server response time when using Universal SSR • Solution: Event Replay export const appConfig: ApplicationConfig = { providers: [ provideClientHydration(withEventReplay()), [...] ], };

Slide 111

Slide 111 text

Hybrid Rendering (Angular 19) • CSR Routes • Regular SPA (without SSR) • Server serves static files • SSR Routes • Live content + Hydration • Server renders the routes • Pre-rendered routes • Built time content + Hydration • Server serves built time rendered { path: 'charts', component: ChartsComponent, renderMode: RenderMode.Client, }, { path: 'home', component: HomeComponent, renderMode: RenderMode.Server, }, { path: post', component: PostComponent, renderMode: RenderMode.Prerender, },

Slide 112

Slide 112 text

Incremental Hydration (Angular 19) • ergonomic API • known from @defer • improve performance • initial load • other CWV • starting in a few weeks export const appConfig: ApplicationConfig = { providers: [ provideClientHydration( withEventReplay(), withPartialHydration() ), [...] ], };

Slide 113

Slide 113 text

hydrate on • hydrate on immediate (default) • hydrate on viewport • hydrate on hover • hydrate on interaction • hydrate on timer(4200ms)

Slide 114

Slide 114 text

hydrate when • specifies an imperative condition as an expression that returns a bool • best used: boolean flag • if the condition returns to false, the swap is not reverted • it is a one-time operation … @defer (hydrate when condition) { } …

Slide 115

Slide 115 text

hydrate never • component will be rendered but will not be hydrated • means that even if the application is fully loaded on the client side, the defer block will remain static and not become interactive … @defer (hydrate never) { } …

Slide 116

Slide 116 text

Demo not yet ☺

Slide 117

Slide 117 text

Recap • Server-Side Rendering • Prerendering / SSG • Hydration • Event Replay • Hybrid Rendering • Incremental Hydration

Slide 118

Slide 118 text

References • Angular Architects Blog • Server-Side Rendering (Blog series) • Angular Docs • Server-side rendering

Slide 119

Slide 119 text

Questions?