|
| 1 | +--- |
| 2 | +description: Vibe coding guidelines and architectural constraints for Angular Advanced Performance within the frontend domain. |
| 3 | +technology: Angular |
| 4 | +domain: frontend |
| 5 | +level: Senior/Architect |
| 6 | +version: "20" |
| 7 | +tags: [performance, advanced, angular, best-practices, clean-code, scalable-code] |
| 8 | +ai_role: Senior Angular Performance Expert |
| 9 | +last_updated: 2026-03-22 |
| 10 | +--- |
| 11 | + |
| 12 | +# 🚀 Angular Advanced Performance Best Practices & Expert Patterns |
| 13 | + |
| 14 | +# 📖 Context & Scope |
| 15 | +- **Primary Goal:** Enforce strict adherence to advanced performance best practices. |
| 16 | +- **Target Tooling:** Cursor, Windsurf, Antigravity. |
| 17 | +- **Tech Stack Version:** Angular 20 |
| 18 | + |
| 19 | +## III. Advanced Performance (31-45) |
| 20 | + |
| 21 | +## 31. Eager Loading of Heavy Components |
| 22 | +**Context:** Bundle Size |
| 23 | +### ❌ Bad Practice |
| 24 | +```html |
| 25 | +<app-chart [data]="data" /> |
| 26 | +``` |
| 27 | +### ⚠️ Problem |
| 28 | +A charting library (e.g., ECharts) loads immediately, blocking TTI (Time to Interactive), even if the chart is below the "fold". |
| 29 | +### ✅ Best Practice |
| 30 | +```html |
| 31 | +@defer (on viewport) { |
| 32 | + <app-chart [data]="data" /> |
| 33 | +} @placeholder { |
| 34 | + <div>Loading chart...</div> |
| 35 | +} |
| 36 | +``` |
| 37 | +### 🚀 Solution |
| 38 | +Use `@defer`. This defers component code loading until a trigger occurs (viewport, interaction, timer). |
| 39 | + |
| 40 | +## 32. Heavy Computation in Main Thread |
| 41 | +**Context:** Event Loop Blocking |
| 42 | +### ❌ Bad Practice |
| 43 | +Sorting an array of 100k elements directly in the component. |
| 44 | +### ⚠️ Problem |
| 45 | +Freezes the UI. |
| 46 | +### ✅ Best Practice |
| 47 | +Offload computations to a Web Worker. |
| 48 | +### 🚀 Solution |
| 49 | +Use Angular Web Workers. In v20, this is easily configured via the CLI. |
| 50 | + |
| 51 | +## 33. Memory Leaks in `effect()` |
| 52 | +**Context:** Signal Effects |
| 53 | +### ❌ Bad Practice |
| 54 | +```typescript |
| 55 | +effect(() => { |
| 56 | + const timer = setInterval(() => ..., 1000); |
| 57 | + // No cleanup |
| 58 | +}); |
| 59 | +``` |
| 60 | +### ⚠️ Problem |
| 61 | +Effects restart when dependencies change. If you don't clean up timers/subscriptions inside an effect, they accumulate. |
| 62 | +### ✅ Best Practice |
| 63 | +```typescript |
| 64 | +effect((onCleanup) => { |
| 65 | + const timer = setInterval(() => ..., 1000); |
| 66 | + onCleanup(() => clearInterval(timer)); |
| 67 | +}); |
| 68 | +``` |
| 69 | +### 🚀 Solution |
| 70 | +Always use the `onCleanup` callback to release resources. |
| 71 | + |
| 72 | +## 34. Excessive Change Detection with `NgZone.run()` |
| 73 | +**Context:** Zone Integration |
| 74 | +### ❌ Bad Practice |
| 75 | +Wrapping third-party libraries in `ngZone.run()` unnecessarily. |
| 76 | +### ⚠️ Problem |
| 77 | +Forces redundant checks of the entire component tree. |
| 78 | +### ✅ Best Practice |
| 79 | +```typescript |
| 80 | +ngZone.runOutsideAngular(() => { |
| 81 | + // Heavy chart rendering or canvas animation |
| 82 | +}); |
| 83 | +``` |
| 84 | +### 🚀 Solution |
| 85 | +Run frequent events (scroll, mousemove, animationFrame) *outside* the Angular zone. Update signals only when UI updates are required. |
| 86 | + |
| 87 | +## 35. Signals equality check default |
| 88 | +**Context:** Signal Performance |
| 89 | +### ❌ Bad Practice |
| 90 | +```typescript |
| 91 | +data = signal({ id: 1 }, { equal: undefined }); // Default checks reference |
| 92 | +``` |
| 93 | +### ⚠️ Problem |
| 94 | +If you create a new object with the same data `{ id: 1 }`, the signal triggers an update, even though the data hasn't fundamentally changed. |
| 95 | +### ✅ Best Practice |
| 96 | +```typescript |
| 97 | +import { isEqual } from 'lodash-es'; |
| 98 | +data = signal(obj, { equal: isEqual }); |
| 99 | +``` |
| 100 | +### 🚀 Solution |
| 101 | +Use a custom comparison function for complex objects to avoid redundant re-renders. |
| 102 | + |
| 103 | +## 36. Lacking `trackBy` in iterables |
| 104 | +**Context:** Re-rendering Lists |
| 105 | +### ❌ Bad Practice |
| 106 | +```html |
| 107 | +<li *ngFor="let item of items">{{ item }}</li> |
| 108 | +``` |
| 109 | +### ⚠️ Problem |
| 110 | +Without tracking, any array change leads to the recreation of all DOM nodes in the list. $O(n)$ DOM operations. |
| 111 | +### ✅ Best Practice |
| 112 | +```html |
| 113 | +@for (item of items; track item.id) |
| 114 | +``` |
| 115 | +### 🚀 Solution |
| 116 | +Always use a unique key in `track`. This allows Angular to move DOM nodes instead of recreating them. |
| 117 | + |
| 118 | +## 37. Recursive Template without Caching |
| 119 | +**Context:** Tree Rendering |
| 120 | +### ❌ Bad Practice |
| 121 | +Recursive component call without `OnPush` and memoization. |
| 122 | +### ⚠️ Problem |
| 123 | +Exponential growth in change detection checks. |
| 124 | +### ✅ Best Practice |
| 125 | +Using the `Memoization` pattern or `computed()` to prepare the tree data structure. |
| 126 | + |
| 127 | +## 38. Global Styles Leakage |
| 128 | +**Context:** CSS Encapsulation |
| 129 | +### ❌ Bad Practice |
| 130 | +```css |
| 131 | +/* global.css */ |
| 132 | +button { padding: 10px; } |
| 133 | +``` |
| 134 | +### ⚠️ Problem |
| 135 | +Global styles unpredictably affect components. |
| 136 | +### ✅ Best Practice |
| 137 | +Use `ViewEncapsulation.Emulated` (default) and specific selectors. |
| 138 | +### 🚀 Solution |
| 139 | +Keep styles locally within components. |
| 140 | + |
| 141 | +## 39. Large Component Bundle |
| 142 | +**Context:** Split Chunks |
| 143 | +### ❌ Bad Practice |
| 144 | +A single huge component of 3000 lines. |
| 145 | +### ⚠️ Problem |
| 146 | +Poor readability, rendering lazy loading of UI parts impossible. |
| 147 | +### ✅ Best Practice |
| 148 | +Decompose into "dumb" (UI) and "smart" components. |
| 149 | +### 🚀 Solution |
| 150 | +Break down the UI into small, reusable blocks. |
| 151 | + |
| 152 | +## 40. Image Optimization Ignorance |
| 153 | +**Context:** Core Web Vitals (LCP) |
| 154 | +### ❌ Bad Practice |
| 155 | +```html |
| 156 | +<img src="large-hero.jpg" /> |
| 157 | +``` |
| 158 | +### ⚠️ Problem |
| 159 | +The browser loads the full image, shifting the layout (CLS). |
| 160 | +### ✅ Best Practice |
| 161 | +```html |
| 162 | +<img ngSrc="hero.jpg" width="800" height="600" priority /> |
| 163 | +``` |
| 164 | +### 🚀 Solution |
| 165 | +Use the `NgOptimizedImage` directive. It automatically handles lazy loading, preconnect, and srcset. |
| 166 | + |
| 167 | +## 41. Hydration Mismatch |
| 168 | +**Context:** SSR / SSG |
| 169 | +### ❌ Bad Practice |
| 170 | +Rendering `Date.now()` or random numbers (`Math.random()`) directly in the template. |
| 171 | +### ⚠️ Problem |
| 172 | +The server generates one number, the client another. This causes "flickering" and a hydration error; Angular discards the server DOM and renders from scratch. |
| 173 | +### ✅ Best Practice |
| 174 | +Use stable data or defer random generation until `afterNextRender`. |
| 175 | +### 🚀 Solution |
| 176 | +Pay attention to template determinism with SSR. |
| 177 | + |
| 178 | +## 42. Synchronous `inject()` inside loops |
| 179 | +**Context:** DI Performance |
| 180 | +### ❌ Bad Practice |
| 181 | +Calling `inject()` inside a function that loops. |
| 182 | +### ⚠️ Problem |
| 183 | +Although `inject` is fast, in hot paths these are unnecessary DI tree lookups. |
| 184 | +### ✅ Best Practice |
| 185 | +Inject dependency once at the class/file constant level. |
| 186 | + |
| 187 | +## 43. Unused Signal Dependencies |
| 188 | +**Context:** Signal Graph |
| 189 | +### ❌ Bad Practice |
| 190 | +Reading a signal inside `computed` whose value doesn't affect the result (an unexecuted logical branch). |
| 191 | +### ⚠️ Problem |
| 192 | +Angular dynamically builds the dependency graph. If you accidentally read a signal, it becomes a dependency. |
| 193 | +### ✅ Best Practice |
| 194 | +Use `untracked()` to read signals whose changes should not trigger a recalculation. |
| 195 | + |
| 196 | +## 44. Excessive Wrappers (`div` soup) |
| 197 | +**Context:** DOM Size |
| 198 | +### ❌ Bad Practice |
| 199 | +```html |
| 200 | +<div><div><div><app-comp></app-comp></div></div></div> |
| 201 | +``` |
| 202 | +### ⚠️ Problem |
| 203 | +Increases DOM tree depth, slowing down Style Recalculation and Layout. |
| 204 | +### ✅ Best Practice |
| 205 | +Use `<ng-container>` to group elements without creating extra DOM nodes. |
| 206 | + |
| 207 | +## 45. Neglecting `runOutsideAngular` for Events |
| 208 | +**Context:** High-frequency events |
| 209 | +### ❌ Bad Practice |
| 210 | +`@HostListener('window:scroll')` |
| 211 | +### ⚠️ Problem |
| 212 | +Every scroll event triggers Change Detection. |
| 213 | +### ✅ Best Practice |
| 214 | +Subscribe manually in `runOutsideAngular` and update the signal only when necessary. |
| 215 | + |
| 216 | +--- |
0 commit comments