Angular 19 Zoneless: Wydajnosc i Change Detection bez Zone.js

Przewodnik techniczny po Zoneless Change Detection w Angular 19 i 20. Architektura detekcji zmian oparta na Signals, aktywacja provideZonelessChangeDetection, migracja setTimeout i Reactive Forms, SSR bez Zone.js oraz benchmarki wydajnosci.

Diagram architektury Zoneless Change Detection w Angular z Signals i optymalizacja wydajnosci

Zoneless Change Detection stanowi najbardziej znaczaca zmiane architekturalna w Angular od czasu wprowadzenia standalone components. Calkowite usuniecie Zone.js z aplikacji przeklada sie na mniejsze bundle (okolo 33 KB mniej), redukcje zbednych cykli detekcji zmian o 30-40% oraz czyste stack trace bez szumu generowanego przez Zone.js. Dla programistow Angular w 2026 roku opanowanie tego mechanizmu jest kluczowe zarowno w codziennej pracy, jak i podczas przygotowania do pytan rekrutacyjnych z Angular dotyczacych change detection.

Harmonogram wdrazania Zoneless

Angular 18 wprowadzil Zoneless jako funkcjonalnosc eksperymentalna. Angular 19 udoskonalil eksperymentalne API za pomoca provideExperimentalZonelessChangeDetection(). Angular 20 przeniosl je do wersji stabilnej jako provideZonelessChangeDetection(). Angular 21 uczynil Zoneless domyslnym ustawieniem dla nowych projektow.

Jak dziala Change Detection oparty na Zone.js

Zanim mozna w pelni zrozumiec zalety trybu Zoneless, warto dokladnie przeanalizowac mechanizm, ktory zastepuje. Zone.js dziala poprzez monkey-patching wszystkich asynchronicznych API przegladarki: setTimeout, setInterval, Promise.then, addEventListener, XMLHttpRequest i kilkudziesieciu innych. Za kazdym razem, gdy jedna z tych operacji asynchronicznych zostaje zakonczona, Zone.js powiadamia Angular, ktory nastepnie uruchamia detekcje zmian w calym drzewie komponentow.

Podejscie to ma fundamentalna wade: Zone.js nie posiada zadnej informacji o tym, czy stan aplikacji faktycznie ulegl zmianie. Wywolanie setTimeout wykorzystywane wylacznie do celow animacyjnych nadal wyzwala pelny cykl detekcji zmian. W duzych aplikacjach z setkami komponentow ten narzut staje sie wymierny i odczuwalny.

app.config.ts - Traditional Zone.js setup (Angular 18-19)typescript
import { ApplicationConfig } from '@angular/core';
import { provideZoneChangeDetection } from '@angular/core';

export const appConfig: ApplicationConfig = {
  providers: [
    provideZoneChangeDetection({ eventCoalescing: true }),
    // Zone.js patches ~130+ browser APIs
    // Every async callback triggers change detection
  ]
};

Mechanizm event coalescing (wprowadzony w Angular 14) czesciowo redukuje narzut poprzez laczenie wielu zdarzen w pojedynczy cykl detekcji zmian, jednak rdzen problemu pozostaje niezmieniony: detekcja zmian uruchamia sie znacznie czesciej niz jest to konieczne.

Aktywacja Zoneless Change Detection w Angular 19 i 20

Sciezka migracji rozni sie w zaleznosci od wersji Angular. Angular 19 wykorzystuje eksperymentalne API, natomiast Angular 20 udostepnia wersje stabilna.

app.config.ts - Angular 19 (experimental)typescript
import { ApplicationConfig } from '@angular/core';
import { provideExperimentalZonelessChangeDetection } from '@angular/core';

export const appConfig: ApplicationConfig = {
  providers: [
    provideExperimentalZonelessChangeDetection(),
    // No more Zone.js patching
  ]
};
app.config.ts - Angular 20+ (stable)typescript
import { ApplicationConfig } from '@angular/core';
import { provideZonelessChangeDetection } from '@angular/core';

export const appConfig: ApplicationConfig = {
  providers: [
    provideZonelessChangeDetection(),
  ]
};

Po zmianie providera nalezy usunac zone.js z tablicy polyfills w pliku angular.json zarowno dla targetow build, jak i test, a nastepnie odinstalowac pakiet:

bash
# Remove zone.js polyfill from angular.json build and test targets
# Then uninstall the package
npm uninstall zone.js

Redukcja rozmiaru bundle jest natychmiastowa: Zone.js odpowiada za okolo 33 KB surowego kodu (10 KB po gzip), ktory jest ladowany eagerly przy starcie aplikacji.

Co wyzwala detekcje zmian w trybie Zoneless

Bez Zone.js przechwytujacego kazda operacje asynchroniczna Angular opiera sie na jawnych powiadomieniach. Framework planuje uruchomienie detekcji zmian, gdy wystapi ktorakolwiek z ponizszych sytuacji:

  • Signal odczytywany w szablonie zmieni swoja wartosc
  • Zostanie wywolane ChangeDetectorRef.markForCheck() (automatycznie przez AsyncPipe)
  • Zmieni sie input komponentu poprzez ComponentRef.setInput()
  • Zostanie wykonany callback listenera szablonu lub hosta (click, input itp.)
  • Wczesniej oznaczony jako dirty widok zostanie dolaczony do drzewa komponentow
counter.component.ts - Signal-driven change detectiontypescript
import { Component, signal, computed } from '@angular/core';

@Component({
  selector: 'app-counter',
  template: `
    <div class="counter">
      <button (click)="decrement()">-</button>
      <span>{{ count() }}</span>
      <button (click)="increment()">+</button>
      <p>Double: {{ doubled() }}</p>
    </div>
  `
})
export class CounterComponent {
  // Signal updates automatically notify the template
  count = signal(0);
  doubled = computed(() => this.count() * 2);

  increment() {
    this.count.update(v => v + 1);
    // No markForCheck() needed - signal handles notification
  }

  decrement() {
    this.count.update(v => v - 1);
  }
}

Signals stanowia naturalne uzupelnienie trybu Zoneless. Gdy wartosc sygnalu sie zmienia, Angular wie dokladnie, ktore szablony od niego zaleza, i planuje ukierunkowana detekcje zmian wylacznie dla tych widokow.

OnPush jest zalecany, ale nie wymagany

Przyjecie strategii ChangeDetectionStrategy.OnPush to zalecany krok w kierunku zgodnosci z trybem Zoneless, ale nie jest to wymog bezwzgledny. Domyslna strategia detekcji zmian nadal dziala w trybie Zoneless. Niemniej jednak OnPush zapewnia, ze komponenty sa ponownie renderowane tylko wtedy, gdy zmienia sie ich inputy lub zostanie wywolane markForCheck(), co naturalnie wpisuje sie w model mentalny Zoneless.

Migracja istniejacych aplikacji: typowe pulapki

Przejscie z Zone.js na tryb Zoneless rzadko ogranicza sie do jednolinijkowej zmiany w istniejacych aplikacjach. Kilka wzorcow, ktore opieraly sie na niejawnym zachowaniu Zone.js, wymaga jawnej obslugi.

setTimeout i setInterval nie wyzwalaja juz aktualizacji

W trybie Zone.js callbacki setTimeout automatycznie wyzwalaly detekcje zmian. W trybie Zoneless tak sie nie dzieje.

user-status.component.ts - Before: relies on Zone.jstypescript
@Component({
  selector: 'app-user-status',
  template: `<span>{{ statusMessage }}</span>`
})
export class UserStatusComponent {
  statusMessage = 'Loading...';

  ngOnInit() {
    setTimeout(() => {
      // Zone.js would trigger CD here - zoneless does NOT
      this.statusMessage = 'Ready';
    }, 2000);
  }
}
user-status.component.ts - After: signal-based approachtypescript
import { Component, signal } from '@angular/core';

@Component({
  selector: 'app-user-status',
  template: `<span>{{ statusMessage() }}</span>`
})
export class UserStatusComponent {
  statusMessage = signal('Loading...');

  ngOnInit() {
    setTimeout(() => {
      // Signal update notifies Angular automatically
      this.statusMessage.set('Ready');
    }, 2000);
  }
}

Reactive Forms wymagaja jawnego powiadomienia

Zmiany stanu formularzy za posrednictwem FormControl.setValue() lub patchValue() nie wyzwalaja automatycznie detekcji zmian w trybie Zoneless. Dwa podejscia rozwiazuja ten problem: polaczenie observabli formularza z markForCheck() lub eksponowanie danych formularza poprzez sygnaly.

search.component.ts - Reactive forms with zonelesstypescript
import { Component, inject, signal } from '@angular/core';
import { FormControl, ReactiveFormsModule } from '@angular/forms';
import { toSignal } from '@angular/core/rxjs-interop';
import { debounceTime, distinctUntilChanged } from 'rxjs';

@Component({
  selector: 'app-search',
  imports: [ReactiveFormsModule],
  template: `
    <input [formControl]="searchControl" placeholder="Search..." />
    <p>Results for: {{ searchTerm() }}</p>
  `
})
export class SearchComponent {
  searchControl = new FormControl('');

  // Convert observable to signal for automatic template updates
  searchTerm = toSignal(
    this.searchControl.valueChanges.pipe(
      debounceTime(300),
      distinctUntilChanged()
    ),
    { initialValue: '' }
  );
}

Gotowy na rozmowy o Angular?

Ćwicz z naszymi interaktywnymi symulatorami, flashcards i testami technicznymi.

Server-Side Rendering bez Zone.js

SSR w aplikacjach Angular bez Zone.js wymaga szczegolnej uwagi. Zone.js dotychczas pomagal Angular okreslac, kiedy aplikacja osiagnela stan "stabilny" odpowiedni do serializacji. Bez niego te role przejmuje serwis PendingTasks.

data-loader.component.ts - SSR-compatible async loadingtypescript
import { Component, inject, signal } from '@angular/core';
import { PendingTasks } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { firstValueFrom } from 'rxjs';

@Component({
  selector: 'app-data-loader',
  template: `
    @if (data()) {
      <div>{{ data()!.title }}</div>
    } @else {
      <div>Loading...</div>
    }
  `
})
export class DataLoaderComponent {
  private http = inject(HttpClient);
  private pendingTasks = inject(PendingTasks);
  data = signal<{ title: string } | null>(null);

  ngOnInit() {
    // PendingTasks.run() prevents SSR serialization until complete
    this.pendingTasks.run(async () => {
      const result = await firstValueFrom(
        this.http.get<{ title: string }>('/api/data')
      );
      this.data.set(result);
    });
  }
}

Bez PendingTasks Angular dokonalby serializacji strony przed zaladowaniem danych asynchronicznych, wysylajac do klienta pusta tresc zamiast prawidlowo wyrenderowanej strony.

Benchmarki wydajnosci: Zone.js kontra Zoneless

Korzysci wydajnosciowe wynikajace z usuniecia Zone.js mozna podzielic na trzy kategorie:

| Metryka | Zone.js | Zoneless | Poprawa | |---------|---------|----------|---------| | Poczatkowy rozmiar bundle | +33KB surowo / +10KB gzip | 0KB narzutu | 100% redukcja | | Cykle detekcji zmian (typowa aplikacja) | 150-300 na interakcje | 5-15 na interakcje | 80-95% mniej | | Glebokosc stack trace | 8-12 dodatkowych ramek Zone | Czyste natywne trace | Natychmiastowa czytelnosc | | Time to Interactive (TTI) | Baseline | 15-25% szybszy | Eliminacja bootstrapu Zone |

Najbardziej spektakularna poprawa widoczna jest w aplikacjach z intensywna aktywnoscia asynchroniczna: polling HTTP, polaczenia WebSocket, timery i zlozone handlery zdarzen. Kazda z tych operacji wczesniej wyzwalala zbedne cykle detekcji zmian, ktore tryb Zoneless calkowicie eliminuje.

Kompatybilnosc bibliotek

Niektore biblioteki zewnetrzne nadal wewnetrznie polegaja na Zone.js. Okna dialogowe, pewne wrappery Web Components i niektore biblioteki animacji moga zachowywac sie nieprzewidywalnie w trybie Zoneless. Przed wdrozeniem trybu Zoneless na srodowisko produkcyjne nalezy dokladnie przetestowac wszystkie integracje z bibliotekami zewnetrznymi.

API NgZone, ktore przetrwaja migracje

Czeste nieporozumienie: usuniecie Zone.js oznacza koniecznosc usuniecia wszystkich odwolan do NgZone. Jest to nieprawda. Metody NgZone.run() i NgZone.runOutsideAngular() pozostaja kompatybilne z aplikacjami Zoneless i powinny byc zachowane w bibliotekach wspoldzielonych. Ich usuniecie moze spowodowac regresje wydajnosciowe w aplikacjach, ktore nadal uzywaja Zone.js i korzystaja z tych bibliotek.

Jednak trzy observable NgZone musza zostac wyeliminowane:

  • NgZone.onMicrotaskEmpty -- nigdy nie emituje w trybie Zoneless
  • NgZone.onUnstable -- nigdy nie emituje w trybie Zoneless
  • NgZone.onStable -- nigdy nie emituje w trybie Zoneless

Logike zaleznac od czasu, ktora wykorzystywala te observable, nalezy zastapic uzyciem afterNextRender() lub afterEveryRender() z @angular/core.

Od Angular 19 Experimental do Angular 21 Default

Ewolucja API Zoneless podaza wyrazna sciezka stabilizacji:

  • Angular 18.1: Wprowadzenie provideExperimentalZonelessChangeDetection() jako API eksperymentalne
  • Angular 19: Udoskonalenie eksperymentalnego API, szersze testy ekosystemu
  • Angular 20: Zmiana nazwy na provideZonelessChangeDetection(), przeniesienie do wersji stabilnej
  • Angular 20.2: API w pelni stabilne, bez oczekiwanych zmian behawioralnych
  • Angular 21: Zoneless staje sie domyslnym ustawieniem dla projektow tworzonych przez ng new, bez koniecznosci jawnego wywolania providera

Dla zespolow pracujacych z Angular 19 sciezka aktualizacji jest prosta: aktualizacja do Angular 20, zastapienie provideExperimentalZonelessChangeDetection przez provideZonelessChangeDetection i usuniecie polyfilla zone.js. Angular 19 osiaga koniec wsparcia 19 maja 2026 roku, co nadaje tej migracji pilny charakter.

Przygotowanie do rozmow rekrutacyjnych z Angular? Modul pytan rekrutacyjnych Angular dotyczacych change detection na SharpSkill szczegolowo omawia wzorce detekcji zmian, w tym scenariusze Zoneless, o ktore rekruterzy pytaja coraz czesciej. Szerszy przeglad tematow oferuje przewodnik top 25 pytan rekrutacyjnych Angular. Modul Angular Signals obejmuje z kolei model reaktywnosci, ktory umozliwia dzialanie trybu Zoneless.

Podsumowanie

  • Usuniecie Zone.js eliminuje 33 KB wagi bundle i likwiduje warstwe monkey-patchingu przechwytujaca ponad 130 API przegladarki
  • Angular Signals zapewniaja jawny model reaktywnosci, ktory czyni tryb Zoneless praktycznym rozwiazaniem, automatycznie powiadamiajac szablony o zmianach stanu
  • Migracja wymaga konwersji wzorcow setTimeout/setInterval, Reactive Forms oraz logiki zalezncej od czasu na podejscie oparte na sygnalach lub wywolaniach markForCheck()
  • Aplikacje SSR musza zaadoptowac PendingTasks jako zamiennik mechanizmu detekcji stabilnosci Zone.js
  • Metody NgZone.run() i NgZone.runOutsideAngular() powinny byc zachowane w bibliotekach wspoldzielonych dla zachowania kompatybilnosci wstecznej
  • Eksperymentalne API Angular 19 (provideExperimentalZonelessChangeDetection) mapuje sie bezposrednio na stabilne provideZonelessChangeDetection() z Angular 20, co sprawia, ze aktualizacja sprowadza sie do zmiany nazwy
  • Kompatybilnosc bibliotek zewnetrznych pozostaje glownym czynnikiem ryzyka i wymaga gruntownego testowania przed wdrozeniem produkcyjnym

Zacznij ćwiczyć!

Sprawdź swoją wiedzę z naszymi symulatorami rozmów i testami technicznymi.

Tagi

#angular
#zoneless
#change-detection
#performance
#signals
#zone-js

Udostępnij

Powiązane artykuły