Как работает функция поиска Angular 2?
В Angular 1 обнаружение изменений было связано с грязной проверкой иерархии $scope. Мы будем неявно или явно создавать наблюдателей в наших шаблонах, контроллерах или компонентах.
В Angular 2 у нас больше нет $scope, но мы переопределяем setInterval, setTimeout и др. Я могу видеть, как Angular может использовать это для запуска $digest, но как Angular определяет, что изменилось, особенно учитывая, что Object.observe никогда не попадал в браузер?
Пример
Вот простой пример. Объект, определенный в службе, обновляется в setInterval. DOM перекомпилирует каждый интервал.
Как Angular может сказать, что AppComponent наблюдает за службой и что значение атрибута службы изменилось?
var InjectedService = function() {
var val = {a:1}
setInterval(() => val.a++, 1000);
return val;
}
var AppComponent = ng.core
.Component({
selector: "app",
template:
`
{{service.a}}
`
})
.Class({
constructor: function(service) {
this.service = service;
}
})
AppComponent.parameters = [ new ng.core.Inject( InjectedService ) ];
document.addEventListener('DOMContentLoaded', function() {
ng.platform.browser.bootstrap(AppComponent, [InjectedService])
});
Ответы
Ответ 1
Angular создает объект-детектор изменений (см. ChangeDetectorRef) для каждого компонента, который отслеживает последнее значение каждой привязки шаблона, например как {{service.a}}
. По умолчанию после каждого асинхронного события браузера (например, ответа с сервера или события клика или события тайм-аута), Angular обнаружение изменений выполняет и проверяет каждую привязку с помощью этих объектов-детекторов изменения.
Если обнаружено изменение, это изменение распространяется. Например.
- Если значение свойства ввода изменилось, новое значение будет передано в свойство ввода компонента.
- Если значение привязки
{{}}
изменилось, новое значение распространяется на свойство DOM textContent
.
- Если значение
x
изменяется в стиле, атрибуте или привязке класса – то есть [style.x]
или [attr.x]
или [class.x]
– новое значение распространяется на DOM для обновления стиля, атрибута HTML или класса.
Angular использует Zone.js для создания своей собственной зоны (NgZone), в которой обезьяна исправляет все асинхронные события (браузер DOM события, тайм-ауты, AJAX/XHR). Таким образом, обнаружение изменений может автоматически запускаться после каждого асинхронного события. I.e, после завершения каждого обработчика (функции) асинхронного события будет выполняться изменение обнаружения Angular.
У меня есть намного больше деталей и ссылок ссылки в этом ответе: Что такое Angular2, эквивалентный часам AngularJS $?
Ответ 2
Zone.js
Изменения происходят как реакция на что-то, поэтому в этом отношении они асинхронны. Они вызваны асинхронными действиями, и в мире браузера они События. Чтобы перехватить эти события, angular использует zone.js, который исправляет стек вызовов JavaScript (я верю, кто-то поправьте меня, если я ошибаюсь) и выставляет крючки, которые могут быть используется для выполнения других действий.
function angular() {...}
zone.run(angular);
Если вы представляете, что эта функция angular
- это весь Angular, это будет то, как она выполняется в зоне. Поступая таким образом, События могут быть перехвачены, и если они запущены, мы можем предположить, что произойдут изменения, и выслушайте их/наблюдайте за ними.
ApplicationRef
В действительности ApplicationRef
создает зону:
/**
* Create an Angular zone.
*/
export function createNgZone(): NgZone {
return new NgZone({enableLongStackTrace: assertionsEnabled()});
}
и класс NgZone
создается с помощью нескольких эмитентов событий:
this._onTurnStartEvents = new EventEmitter(false);
this._onTurnDoneEvents = new EventEmitter(false);
this._onEventDoneEvents = new EventEmitter(false);
this._onErrorEvents = new EventEmitter(false);
который он предоставляет внешнему миру через getters:
get onTurnStart(): /* Subject */ any { return this._onTurnStartEvents; }
get onTurnDone() { return this._onTurnDoneEvents; }
get onEventDone() { return this._onEventDoneEvents; }
get onError() { return this._onErrorEvents; }
Когда ApplicationRef
создан, он подписывается на события зоны, в частности onTurnDone()
:
this.zone.onTurnDone
.subscribe(() => this.zone.run(() => this.tick());
Изменения
Когда запускаются события tick()
, функция запускается через каждый компонент:
this._changeDetectorRefs.forEach((detector) => detector.detectChanges());
и обнаруживает изменения на основе компонентов ChangeDetectionStrategy
. Эти изменения собираются как массив объектов SimpleChange
:
addChange(changes: {[key: string]: any}, oldValue: any, newValue: any): {[key: string]: any} {
if (isBlank(changes)) {
changes = {};
}
changes[this._currentBinding().name] = ChangeDetectionUtil.simpleChange(oldValue, newValue);
return changes;
}
witch доступен для нас через onChanges
интерфейс:
export interface OnChanges {
ngOnChanges(changes: {[key: string]: SimpleChange});
}