Лучший опыт

Новая эра Angular: беззоновое обнаружение изменений.

Фреймворк Angular всегда был на передовом крае разработки веб-приложений. Недавним новшеством команды Angular на пути к повышению эффективности и оптимизации производительности стала функция с интригующим названием provideZonelessChangeDetection. Ее внедрение предвещает переход к “беззоновому” будущему. Разберемся в технических особенностях этой функции и осознаем последствия ее применения. Основная функциональность Функция Основная функциональность

Функция provideZonelessChangeDetection  —  это существенное дополнение к инструментарию Angular. По сути, она настраивает два основных провайдера в среде Angular:

export function provideZonelessChangeDetection(): EnvironmentProviders {
return makeEnvironmentProviders([
{
provide: ChangeDetectionScheduler,
useExisting: ChangeDetectionSchedulerImpl
},
{
provide: NgZone,
useClass: NoopNgZone
}
]);
}
  1. Стратегия обнаружения изменений. Устанавливает ChangeDetectionScheduler для использования ChangeDetectionSchedulerImpl, класса, предназначенного для управления циклами обнаружения изменений.
  2. Оптимизация NgZone. Заменяет стандартный провайдер NgZone на NoopNgZone.

Класс ChangeDetectionSchedulerImpl прост, элегантен и в то же время мощен. Он представляет метод notify, который настраивает таймер для вызова метода ApplicationRef.tick(). Этот метод очень важен, поскольку он запускает обнаружение изменений сверху донизу во всем приложении.

Ниже  —  пример такой реализации:

@Injectable({ providedIn: 'root' })
class ChangeDetectionSchedulerImpl implements ChangeDetectionScheduler {
private appRef = inject(ApplicationRef);
private taskService = inject(PendingTasks);
private pendingRenderTaskId: number|null = null;

notify(): void {
if (this.pendingRenderTaskId !== null) return;

this.pendingRenderTaskId = this.taskService.add();
setTimeout(() => {
try {
if (!this.appRef.destroyed) {
this.appRef.tick();
}
} finally {
// Код не приводится для краткости
}
});
}
}

Функция notify играет ключевую роль во внутренних функциях Angular markViewDirty и markAncestorsForTraversal, которые срабатывают в различных сценариях, таких как markForCheck, события шаблона, setInput, обновления сигналов и т. д.

Чтобы проиллюстрировать практическое применение новой функциональности, рассмотрим следующий пример с dummy-компонентом:

@Component({
selector: 'app-foo',
standalone: true,
imports: [AsyncPipe],
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
<p>markForCheck: {{ markForCheck }}</p>
<p>signal: {{ sig() }}</p>
<p>byEvent: {{ byEvent }}</p>
<p>Async pipe: {{ num$ | async }}</p>
<button (click)="byEvent = 3">Click</button>`,
})
export class FooComponent {
markForCheck = 1;
sig = signal(1);
byEvent = 2;
num$ = interval(1500);

private cdr = inject(ChangeDetectorRef);

ngOnInit() {
setInterval(() => {
this.markForCheck += 1;
this.cdr.markForCheck();
}, 1000);

setTimeout(() => {
this.sig.set(2);
}, 500);
}
}
Нет полифилов  —  нет zone.js

Указанный пример демонстрирует, как можно легко интегрировать новую функциональность в стандартные рабочие процессы Angular. Она представляет собой беззоновую альтернативу без ущерба для отзывчивости и функциональности приложения.

Это достижение весьма примечательно. Его внедрение означает, что использование только сигналов для создания беззоновых приложений больше не является необходимостью. При условии, что приложение использует конвейер async или метод markForCheck в сочетании с наблюдаемыми переменными, оно должно продолжать работать как положено.

Однако важно отметить, что в отсутствие автоматического обнаружения изменений при наличии механизма zone.js, необходимо явно запустить процесс такого обнаружения с помощью одного из предписанных методов. Если этого не сделать, обнаружение изменений не будет выполнено.

Важные соображения

Следует помнить, что функция provideZonelessChangeDetection все еще находится на начальной стадии развития. Ее еще предстоит проверить и по необходимости внести в нее некоторые изменения. Разработчики, заинтересованные в экспериментах с этой функцией, могут использовать последние сборки Angular и применять конструкцию { import ɵprovideZonelessChangeDetection as provideZonelessChangeDetection } from '@angular/core'.