Generating a Zoneless Angular App

Why Use Zoneless? Removing ZoneJS from an Angular application offers several advantages: Improved performance: ZoneJS frequently triggers application synchronization, even when the state hasn't changed. Better Core Web Vitals: Eliminating ZoneJS reduces payload size and startup time. Easier debugging: ZoneJS makes stack traces harder to interpret. Enhanced ecosystem compatibility: Some browser APIs and third-party libraries are incompatible with ZoneJS, making maintenance more complex. Enabling Zoneless in an Application The API for zoneless mode is experimental and subject to change. To enable zoneless mode: Standalone Bootstrap bootstrapApplication(MyApp, {providers: [ provideExperimentalZonelessChangeDetection(), ]}); NgModule Bootstrap platformBrowser().bootstrapModule(AppModule); @NgModule({ providers: [provideExperimentalZonelessChangeDetection()] }) export class AppModule {} Removing ZoneJS To remove ZoneJS from an Angular project: Remove zone.js and zone.js/testing from the angular.json polyfills. If using polyfills.ts, delete: import 'zone.js'; import 'zone.js/testing'; Uninstall ZoneJS from dependencies: npm uninstall zone.js Requirements for Zoneless Compatibility Angular determines when to run change detection through specific notifications, such as: ChangeDetectorRef.markForCheck (automatically triggered by AsyncPipe) ComponentRef.setInput Updating a signal referenced in a template Template event listeners Attaching views marked as dirty OnPush-Compatible Components Using ChangeDetectionStrategy.OnPush is recommended for compatibility. While not mandatory, it ensures Angular efficiently detects updates without relying on ZoneJS. Removing Incompatible NgZone Features The following APIs do not work in zoneless mode and should be removed: NgZone.onMicrotaskEmpty NgZone.onUnstable NgZone.isStable NgZone.onStable Replace them with: afterNextRender() for single change detection waiting afterRender() for conditions spanning multiple cycles MutationObserver for direct DOM state monitoring Note: NgZone.run and NgZone.runOutsideAngular are still supported. PendingTasks for Server-Side Rendering (SSR) ZoneJS helps determine when SSR should serialize the page. Without ZoneJS, use the PendingTasks service to track async tasks: const taskService = inject(PendingTasks); const taskCleanup = taskService.add(); await doSomeWorkThatNeedsToBeRendered(); taskCleanup(); Angular uses this internally for routing and HTTP requests. Testing and Debugging Using Zoneless in TestBed To test a zoneless Angular app: TestBed.configureTestingModule({ providers: [provideExperimentalZonelessChangeDetection()] }); const fixture = TestBed.createComponent(MyComponent); await fixture.whenStable(); Avoid fixture.detectChanges() to mimic real-world change detection. Debug Mode for State Updates Use provideExperimentalCheckNoChangesForDebug() to catch undetected updates. Angular will throw ExpressionChangedAfterItHasBeenCheckedError if a binding changes without notification. By following these steps, you can successfully migrate your Angular app to a zoneless architecture, improving performance and maintainability. Feel free to explore the GitHub repository for more details and to try out the project yourself. Happy coding!

Jan 30, 2025 - 06:55
 0
Generating a Zoneless Angular App

Why Use Zoneless?

Removing ZoneJS from an Angular application offers several advantages:

  • Improved performance: ZoneJS frequently triggers application synchronization, even when the state hasn't changed.
  • Better Core Web Vitals: Eliminating ZoneJS reduces payload size and startup time.
  • Easier debugging: ZoneJS makes stack traces harder to interpret.
  • Enhanced ecosystem compatibility: Some browser APIs and third-party libraries are incompatible with ZoneJS, making maintenance more complex.

Enabling Zoneless in an Application

The API for zoneless mode is experimental and subject to change. To enable zoneless mode:

Standalone Bootstrap

bootstrapApplication(MyApp, {providers: [
  provideExperimentalZonelessChangeDetection(),
]});

NgModule Bootstrap

platformBrowser().bootstrapModule(AppModule);

@NgModule({
  providers: [provideExperimentalZonelessChangeDetection()]
})
export class AppModule {}

Removing ZoneJS

To remove ZoneJS from an Angular project:

  • Remove zone.js and zone.js/testing from the angular.json polyfills.

  • If using polyfills.ts, delete:

   import 'zone.js';
   import 'zone.js/testing';
  • Uninstall ZoneJS from dependencies:
   npm uninstall zone.js

Requirements for Zoneless Compatibility

Angular determines when to run change detection through specific notifications, such as:

  • ChangeDetectorRef.markForCheck (automatically triggered by AsyncPipe)
  • ComponentRef.setInput
  • Updating a signal referenced in a template
  • Template event listeners
  • Attaching views marked as dirty

OnPush-Compatible Components

Using ChangeDetectionStrategy.OnPush is recommended for compatibility. While not mandatory, it ensures Angular efficiently detects updates without relying on ZoneJS.

Removing Incompatible NgZone Features

The following APIs do not work in zoneless mode and should be removed:

  • NgZone.onMicrotaskEmpty
  • NgZone.onUnstable
  • NgZone.isStable
  • NgZone.onStable

Replace them with:

  • afterNextRender() for single change detection waiting
  • afterRender() for conditions spanning multiple cycles
  • MutationObserver for direct DOM state monitoring

Note: NgZone.run and NgZone.runOutsideAngular are still supported.

PendingTasks for Server-Side Rendering (SSR)

ZoneJS helps determine when SSR should serialize the page. Without ZoneJS, use the PendingTasks service to track async tasks:

const taskService = inject(PendingTasks);
const taskCleanup = taskService.add();
await doSomeWorkThatNeedsToBeRendered();
taskCleanup();

Angular uses this internally for routing and HTTP requests.

Testing and Debugging

Using Zoneless in TestBed

To test a zoneless Angular app:

TestBed.configureTestingModule({
  providers: [provideExperimentalZonelessChangeDetection()]
});
const fixture = TestBed.createComponent(MyComponent);
await fixture.whenStable();

Avoid fixture.detectChanges() to mimic real-world change detection.

Debug Mode for State Updates

Use provideExperimentalCheckNoChangesForDebug() to catch undetected updates. Angular will throw ExpressionChangedAfterItHasBeenCheckedError if a binding changes without notification.

By following these steps, you can successfully migrate your Angular app to a zoneless architecture, improving performance and maintainability.

Feel free to explore the GitHub repository for more details and to try out the project yourself. Happy coding!