Angular 2+: Стратегия обнаружения изменений
Стратегия обнаружения изменений — это механизм Angular, который отвечает за изменение в шаблонах при изменении в моделях, то-есть как именно будут выглядеть компоненты в результате различных событий — браузерное события, promices, timers, XHR и т.д.
В Angular для обнаружения изменений используется библиотека Zone.js, которая запускает цикл обнаружения изменений при завершении событий. Метод onMicrotaskEmpty
выполняет tick
, который в свою очередь запускает механизм change detection.
this.zone.onMicrotaskEmpty
.subscribe(() => {
this.zone.run(() => this.tick());
})
В Angular есть 2 метода обнаружения изменения:
- Default
- OnPush
В случае Default стратегии обнаружения изменений, при каких-либо изменениях, проверка будет запущена по всем компонентам. В результате такой проверки новые значения сравниваются со старыми и, в случае их отличия, они будут изменены.
Запуск механизма ChangeDetection в родительском компоненте автоматически инициирует запуск механизма во всех дочерних компонентах. Такая проверка называется «dirty check» («грядной проверкой»). В результате на каждом компоненте Angular будет сравнивать новые значения со старыми и обновлять представление, если они не равны.
Альтернативой существует стратегия изменения OnPush, в результате которой компонент будет зависеть только от входных параметров @Inputs()
.
@Component({
selector: 'app-root',
changeDetection: ChangeDetectionStrategy.OnPush
}))
Числа, булевые, строки, null и undefined это примитивные типы, в таком случае стратегия обнаружения изменения будет запущена сразу по изменению значения параметра Input, а объекты, массивы, и функции передаются по ссылке, таким образом для обнаружения изменений нужно будет изменить ссылку на объект.
Для того, что бы стратегия обнаружения OnPush работала корректно, разберемся что такое mutable и immutable объекты. Отличие immutable объекта заключается в том, что мы не можем изменить свойство объекта без изменения самого объекта, в таком случае достаточно будет сравнить ссылки на объект для Inputs, что бы понять, что объекты не равны.
При работе со стратегией обнаружения OnPush, может возникнуть необходимость вручную запустить обнаружение изменений в компоненте и его потомках, например, при работе с асинхронными операциями.
Есть несколько способов, как именно можно запустить механизм обнаружения изменений:
Метод detectChanges()
класса ChangeDetectorRef — говорит Angular, что нужно запустить процесс обнаружения изменений в этом компоненте и его потомках.
export class AComponent {
@Input() inputAProp;
constructor(public cd: ChangeDetectorRef) {
this.cd.detach();
}
ngOnChanges(values) {
this.cd.detectChanges();
}
Метод tick()
класса ApplicationRef — говорит Angular, что нужно запустить процесс обнаружения изменений во всем приложении.
export class AppComponent {
constructor(private appRef: ApplicationRef) {
...
}
updateApplication() {
this.appRef.tick();
}
}
Метод markForCheck()
класса ChangeDetectorRef — отмечает, что этот компонент должен быть проверен при следующем цикле запуску проверки изменений.
export class AppComponent {
constructor(private cd: ChangeDetectorRef) {
...
}
ngOnInit() {
this.opStream.subscribe(() => {
this.cd.markForCheck();
})
}
}
Кроме того, в классе ChangeDetectorRef есть метод detach
, который позволяет выключить проверку изменений и метод reattach
, позволяющий включить проверку после ее выключения.
Можем подитожить, что если в компоненте включена стратегия изменения OnPush — компонент будет проверен на изменения при:
- Изменении входных параметров Inputs()
- Использовании async pipe в шаблонах
- Мануальном внедрении проверки при помощи методов классов ChangeDetectorRef и ApplicationRef
Источники: angular.io, angular-university.io, bitsrc.io, thoughtram.io, habr.com, www.coldfox.ru, bxnotes.ru, itnext.io, metanit.com